update components

This commit is contained in:
Luke Pulverenti 2016-01-18 14:07:26 -05:00
parent 047fd2b438
commit 6f96e87248
22 changed files with 472 additions and 303 deletions

View file

@ -15,12 +15,12 @@
},
"devDependencies": {},
"ignore": [],
"version": "1.0.20",
"_release": "1.0.20",
"version": "1.0.21",
"_release": "1.0.21",
"_resolution": {
"type": "version",
"tag": "1.0.20",
"commit": "b88c3533403d7fc921adb381c81917c80d9c4e77"
"tag": "1.0.21",
"commit": "dd73237b9d554d45a664e820042804c6d129d280"
},
"_source": "git://github.com/MediaBrowser/emby-webcomponents.git",
"_target": "~1.0.0",

View file

@ -1,4 +1,4 @@
define(['browser'], function (browser) {
define(['browser'], function (browser) {
function canPlayH264() {
var v = document.createElement('video');
@ -67,7 +67,7 @@
function canPlayHls(src) {
if (_canPlayHls == null) {
_canPlayHls = window.MediaSource != null || canPlayNativeHls();
_canPlayHls = canPlayNativeHls() || canPlayHlsWithMSE();
}
return _canPlayHls;
}
@ -83,6 +83,15 @@
return false;
}
function canPlayHlsWithMSE() {
if (window.MediaSource != null) {
// text tracks dont work with this in firefox
return !browser.firefox;
}
return false;
}
return function () {
var bitrateSetting = 100000000;

View file

@ -1,13 +1,13 @@
{
"name": "hls.js",
"version": "0.4.5",
"version": "0.4.6",
"description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js",
"authors": [
"Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>"
],
"main": "dist/hls.js",
"private": true,
"private": false,
"ignore": [
"**/.*",
"node_modules",
@ -15,14 +15,13 @@
"test",
"tests"
],
"_release": "0.4.5",
"_release": "0.4.6",
"_resolution": {
"type": "version",
"tag": "v0.4.5",
"commit": "908ac4a44a182bdbede9c1830828983c18532ca0"
"tag": "v0.4.6",
"commit": "eb4bfb0ebadda9797d8020e7b3a2d2c699444cab"
},
"_source": "git://github.com/dailymotion/hls.js.git",
"_target": "~0.4.5",
"_originalSource": "dailymotion/hls.js",
"_direct": true
"_originalSource": "dailymotion/hls.js"
}

View file

@ -186,6 +186,8 @@ configuration parameters could be provided to hls.js upon instantiation of Hls O
maxBufferLength : 30,
maxMaxBufferLength : 600,
maxBufferSize : 60*1000*1000,
maxBufferHole : 0.3,
maxSeekHole : 2,
liveSyncDurationCount : 3,
liveMaxLatencyDurationCount: 10,
enableWorker : true,
@ -240,6 +242,20 @@ this is the guaranteed buffer length hls.js will try to reach, regardless of max
'minimum' maximum buffer size in bytes. if buffer size upfront is bigger than this value, no fragment will be loaded.
#### ```maxBufferHole```
(default 0.3s)
'maximum' inter-fragment buffer hole tolerance that hls.js can cope with.
When switching between quality level, fragments might not be perfectly aligned.
This could result in small overlapping or hole in media buffer. This tolerance factor helps cope with this.
#### ```maxSeekHole```
(default 2s)
in case playback is stalled, and a buffered range is available upfront, less than maxSeekHole seconds from current media position,
hls.js will jump over this buffer hole to reach the beginning of this following buffered range.
```maxSeekHole``` allows to configure this jumpable threshold.
#### ```maxMaxBufferLength```
(default 600s)
@ -499,7 +515,7 @@ full list of Events available below :
- `Hls.Events.LEVEL_PTS_UPDATED` - fired when a level's PTS information has been updated after parsing a fragment
- data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment }
- `Hls.Events.LEVEL_SWITCH` - fired when a level switch is requested
- data: { levelId : id of new level }
- data: { level : id of new level, it is the index of the array `Hls.levels` }
- `Hls.Events.KEY_LOADING` - fired when a decryption key loading starts
- data: { frag : fragment object}
- `Hls.Events.KEY_LOADED` - fired when a decryption key loading is completed

View file

@ -1,13 +1,13 @@
{
"name": "hls.js",
"version": "0.4.5",
"version": "0.4.6",
"description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js",
"authors": [
"Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>"
],
"main": "dist/hls.js",
"private": true,
"private": false,
"ignore": [
"**/.*",
"node_modules",

View file

@ -718,14 +718,6 @@ function timeRangesToString(r) {
events.buffer.push(event);
refreshCanvas();
var decodedFrames, droppedFrames;
if(navigator.userAgent.toLowerCase().indexOf('firefox') !== -1) {
decodedFrames = v.mozDecodedFrames;
droppedFrames = v.mozParsedFrames-v.mozPresentedFrames;
} else {
decodedFrames = v.webkitDecodedFrameCount;
droppedFrames = v.webkitDroppedFrameCount;
}
var log = "Duration:"
+ v.duration + "<br>"
+ "Buffered:"
@ -733,11 +725,16 @@ function timeRangesToString(r) {
+ "Seekable:"
+ timeRangesToString(v.seekable) + "<br>"
+ "Played:"
+ timeRangesToString(v.played) + "<br>"
+ "Decoded Frames:"
+ decodedFrames + "<br>"
+ "Dropped Frames:"
+ droppedFrames + "<br>";
+ timeRangesToString(v.played) + "<br>";
var videoPlaybackQuality = v.getVideoPlaybackQuality;
if(videoPlaybackQuality && typeof(videoPlaybackQuality) === typeof(Function)) {
log+="Dropped Frames:"+ v.getVideoPlaybackQuality().droppedVideoFrames + "<br>";
log+="Corrupted Frames:"+ v.getVideoPlaybackQuality().corruptedVideoFrames + "<br>";
} else if(v.webkitDroppedFrameCount) {
log+="Dropped Frames:"+ v.webkitDroppedFrameCount + "<br>";
}
$("#buffered_log").html(log);
$("#HlsStats").text(JSON.stringify(sortObject(stats),null,"\t"));
ctx.fillStyle = "blue";

View file

@ -371,34 +371,42 @@ Object.defineProperty(exports, '__esModule', {
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; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
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'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _events = require('../events');
var _events2 = _interopRequireDefault(_events);
var AbrController = (function () {
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var AbrController = (function (_EventHandler) {
_inherits(AbrController, _EventHandler);
function AbrController(hls) {
_classCallCheck(this, AbrController);
this.hls = hls;
_get(Object.getPrototypeOf(AbrController.prototype), 'constructor', this).call(this, hls, _events2['default'].FRAG_LOAD_PROGRESS);
this.lastfetchlevel = 0;
this._autoLevelCapping = -1;
this._nextAutoLevel = -1;
this.onflp = this.onFragmentLoadProgress.bind(this);
hls.on(_events2['default'].FRAG_LOAD_PROGRESS, this.onflp);
}
_createClass(AbrController, [{
key: 'destroy',
value: function destroy() {
this.hls.off(_events2['default'].FRAG_LOAD_PROGRESS, this.onflp);
_eventHandler2['default'].prototype.destroy.call(this);
}
}, {
key: 'onFragmentLoadProgress',
value: function onFragmentLoadProgress(event, data) {
key: 'onFragLoadProgress',
value: function onFragLoadProgress(data) {
var stats = data.stats;
if (stats.aborted === undefined) {
this.lastfetchduration = (performance.now() - stats.trequest) / 1000;
@ -466,12 +474,12 @@ var AbrController = (function () {
}]);
return AbrController;
})();
})(_eventHandler2['default']);
exports['default'] = AbrController;
module.exports = exports['default'];
},{"../events":18}],4:[function(require,module,exports){
},{"../event-handler":18,"../events":19}],4:[function(require,module,exports){
/*
* Level Controller
*/
@ -484,40 +492,40 @@ Object.defineProperty(exports, '__esModule', {
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; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
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'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _events = require('../events');
var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _utilsLogger = require('../utils/logger');
var _errors = require('../errors');
var LevelController = (function () {
var LevelController = (function (_EventHandler) {
_inherits(LevelController, _EventHandler);
function LevelController(hls) {
_classCallCheck(this, LevelController);
this.hls = hls;
this.onml = this.onManifestLoaded.bind(this);
this.onll = this.onLevelLoaded.bind(this);
this.onerr = this.onError.bind(this);
_get(Object.getPrototypeOf(LevelController.prototype), 'constructor', this).call(this, hls, _events2['default'].MANIFEST_LOADED, _events2['default'].LEVEL_LOADED, _events2['default'].ERROR);
this.ontick = this.tick.bind(this);
hls.on(_events2['default'].MANIFEST_LOADED, this.onml);
hls.on(_events2['default'].LEVEL_LOADED, this.onll);
hls.on(_events2['default'].ERROR, this.onerr);
this._manualLevel = this._autoLevelCapping = -1;
}
_createClass(LevelController, [{
key: 'destroy',
value: function destroy() {
var hls = this.hls;
hls.off(_events2['default'].MANIFEST_LOADED, this.onml);
hls.off(_events2['default'].LEVEL_LOADED, this.onll);
hls.off(_events2['default'].ERROR, this.onerr);
if (this.timer) {
clearInterval(this.timer);
}
@ -525,7 +533,7 @@ var LevelController = (function () {
}
}, {
key: 'onManifestLoaded',
value: function onManifestLoaded(event, data) {
value: function onManifestLoaded(data) {
var levels0 = [],
levels = [],
bitrateStart,
@ -626,7 +634,7 @@ var LevelController = (function () {
}
}, {
key: 'onError',
value: function onError(event, data) {
value: function onError(data) {
if (data.fatal) {
return;
}
@ -688,7 +696,7 @@ var LevelController = (function () {
}
}, {
key: 'onLevelLoaded',
value: function onLevelLoaded(event, data) {
value: function onLevelLoaded(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
@ -769,12 +777,12 @@ var LevelController = (function () {
}]);
return LevelController;
})();
})(_eventHandler2['default']);
exports['default'] = LevelController;
module.exports = exports['default'];
},{"../errors":17,"../events":18,"../utils/logger":28}],5:[function(require,module,exports){
},{"../errors":17,"../event-handler":18,"../events":19,"../utils/logger":29}],5:[function(require,module,exports){
/*
* MSE Media Controller
*/
@ -787,10 +795,14 @@ Object.defineProperty(exports, '__esModule', {
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; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
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'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _demuxDemuxer = require('../demux/demuxer');
var _demuxDemuxer2 = _interopRequireDefault(_demuxDemuxer);
@ -799,6 +811,10 @@ var _events = require('../events');
var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _utilsLogger = require('../utils/logger');
var _utilsBinarySearch = require('../utils/binary-search');
@ -825,42 +841,27 @@ var State = {
BUFFER_FLUSHING: 8
};
var MSEMediaController = (function () {
var MSEMediaController = (function (_EventHandler) {
_inherits(MSEMediaController, _EventHandler);
function MSEMediaController(hls) {
_classCallCheck(this, MSEMediaController);
_get(Object.getPrototypeOf(MSEMediaController.prototype), 'constructor', this).call(this, hls, _events2['default'].MEDIA_ATTACHING, _events2['default'].MEDIA_DETACHING, _events2['default'].MANIFEST_PARSED, _events2['default'].LEVEL_LOADED, _events2['default'].KEY_LOADED, _events2['default'].FRAG_LOADED, _events2['default'].FRAG_PARSING_INIT_SEGMENT, _events2['default'].FRAG_PARSING_DATA, _events2['default'].FRAG_PARSED, _events2['default'].ERROR);
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);
// internal listeners
this.onmediaatt0 = this.onMediaAttaching.bind(this);
this.onmediadet0 = this.onMediaDetaching.bind(this);
this.onmp = this.onManifestParsed.bind(this);
this.onll = this.onLevelLoaded.bind(this);
this.onfl = this.onFragLoaded.bind(this);
this.onkl = this.onKeyLoaded.bind(this);
this.onis = this.onInitSegment.bind(this);
this.onfpg = this.onFragParsing.bind(this);
this.onfp = this.onFragParsed.bind(this);
this.onerr = this.onError.bind(this);
this.ontick = this.tick.bind(this);
hls.on(_events2['default'].MEDIA_ATTACHING, this.onmediaatt0);
hls.on(_events2['default'].MEDIA_DETACHING, this.onmediadet0);
hls.on(_events2['default'].MANIFEST_PARSED, this.onmp);
}
_createClass(MSEMediaController, [{
key: 'destroy',
value: function destroy() {
this.stop();
var hls = this.hls;
hls.off(_events2['default'].MEDIA_ATTACHING, this.onmediaatt0);
hls.off(_events2['default'].MEDIA_DETACHING, this.onmediadet0);
hls.off(_events2['default'].MANIFEST_PARSED, this.onmp);
_eventHandler2['default'].prototype.destroy.call(this);
this.state = State.IDLE;
}
}, {
@ -894,13 +895,6 @@ var MSEMediaController = (function () {
this.timer = setInterval(this.ontick, 100);
this.level = -1;
this.fragLoadError = 0;
hls.on(_events2['default'].FRAG_LOADED, this.onfl);
hls.on(_events2['default'].FRAG_PARSING_INIT_SEGMENT, this.onis);
hls.on(_events2['default'].FRAG_PARSING_DATA, this.onfpg);
hls.on(_events2['default'].FRAG_PARSED, this.onfp);
hls.on(_events2['default'].ERROR, this.onerr);
hls.on(_events2['default'].LEVEL_LOADED, this.onll);
hls.on(_events2['default'].KEY_LOADED, this.onkl);
}
}, {
key: 'stop',
@ -935,14 +929,6 @@ var MSEMediaController = (function () {
this.demuxer.destroy();
this.demuxer = null;
}
var hls = this.hls;
hls.off(_events2['default'].FRAG_LOADED, this.onfl);
hls.off(_events2['default'].FRAG_PARSED, this.onfp);
hls.off(_events2['default'].FRAG_PARSING_DATA, this.onfpg);
hls.off(_events2['default'].LEVEL_LOADED, this.onll);
hls.off(_events2['default'].KEY_LOADED, this.onkl);
hls.off(_events2['default'].FRAG_PARSING_INIT_SEGMENT, this.onis);
hls.off(_events2['default'].ERROR, this.onerr);
}
}, {
key: 'tick',
@ -1001,7 +987,7 @@ var MSEMediaController = (function () {
// we are not at playback start, get next load level from level Controller
level = hls.nextLoadLevel;
}
var bufferInfo = this.bufferInfo(pos, 0.3),
var bufferInfo = this.bufferInfo(pos, this.config.maxBufferHole),
bufferLen = bufferInfo.len,
bufferEnd = bufferInfo.end,
fragPrevious = this.fragPrevious,
@ -1020,7 +1006,9 @@ var MSEMediaController = (function () {
this.level = level;
levelDetails = this.levels[level].details;
// if level info not retrieved yet, switch state and wait for level retrieval
if (typeof levelDetails === 'undefined') {
// if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load
// a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist)
if (typeof levelDetails === 'undefined' || levelDetails.live && this.levelLastLoaded !== level) {
this.state = State.WAITING_LEVEL;
break;
}
@ -1176,7 +1164,7 @@ var MSEMediaController = (function () {
}
pos = v.currentTime;
var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate;
var bufferStarvationDelay = this.bufferInfo(pos, 0.3).end - pos;
var bufferStarvationDelay = this.bufferInfo(pos, this.config.maxBufferHole).end - pos;
var fragLevelNextLoadedDelay = frag.duration * this.levels[hls.nextLoadLevel].bitrate / (8 * loadRate); //bps/Bps
/* if we have less than 2 frag duration in buffer and if frag loaded delay is greater than buffer starvation delay
... and also bigger than duration needed to load fragment at next level ...*/
@ -1363,6 +1351,7 @@ var MSEMediaController = (function () {
bufferLen = bufferEnd - pos;
} else if (pos + maxHoleDuration < start) {
bufferStartNext = start;
break;
}
}
return { len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext };
@ -1605,7 +1594,7 @@ var MSEMediaController = (function () {
}
}, {
key: 'onMediaAttaching',
value: function onMediaAttaching(event, data) {
value: function onMediaAttaching(data) {
var media = this.media = data.media;
// setup the media source
var ms = this.mediaSource = new MediaSource();
@ -1680,7 +1669,7 @@ var MSEMediaController = (function () {
if (this.state === State.FRAG_LOADING) {
// check if currently loaded fragment is inside buffer.
//if outside, cancel fragment loading, otherwise do nothing
if (this.bufferInfo(this.media.currentTime, 0.3).len === 0) {
if (this.bufferInfo(this.media.currentTime, this.config.maxBufferHole).len === 0) {
_utilsLogger.logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
var fragCurrent = this.fragCurrent;
if (fragCurrent) {
@ -1732,7 +1721,7 @@ var MSEMediaController = (function () {
}
}, {
key: 'onManifestParsed',
value: function onManifestParsed(event, data) {
value: function onManifestParsed(data) {
var aac = false,
heaac = false,
codecs;
@ -1761,13 +1750,14 @@ var MSEMediaController = (function () {
}
}, {
key: 'onLevelLoaded',
value: function onLevelLoaded(event, data) {
value: function onLevelLoaded(data) {
var newDetails = data.details,
newLevelId = data.level,
curLevel = this.levels[newLevelId],
duration = newDetails.totalduration;
_utilsLogger.logger.log('level ' + newLevelId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration);
this.levelLastLoaded = newLevelId;
if (newDetails.live) {
var curDetails = curLevel.details;
@ -1816,7 +1806,7 @@ var MSEMediaController = (function () {
}
}, {
key: 'onFragLoaded',
value: function onFragLoaded(event, data) {
value: function onFragLoaded(data) {
var fragCurrent = this.fragCurrent;
if (this.state === State.FRAG_LOADING && fragCurrent && data.frag.level === fragCurrent.level && data.frag.sn === fragCurrent.sn) {
if (this.fragBitrateTest === true) {
@ -1854,8 +1844,8 @@ var MSEMediaController = (function () {
this.fragLoadError = 0;
}
}, {
key: 'onInitSegment',
value: function onInitSegment(event, data) {
key: 'onFragParsingInitSegment',
value: function onFragParsingInitSegment(data) {
if (this.state === State.PARSING) {
// 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
@ -1913,8 +1903,8 @@ var MSEMediaController = (function () {
}
}
}, {
key: 'onFragParsing',
value: function onFragParsing(event, data) {
key: 'onFragParsingData',
value: function onFragParsingData(data) {
if (this.state === State.PARSING) {
this.tparse2 = Date.now();
var level = this.levels[this.level],
@ -1931,7 +1921,7 @@ var MSEMediaController = (function () {
//trigger handler right now
this.tick();
} else {
_utilsLogger.logger.warn('not in PARSING state, discarding ' + event);
_utilsLogger.logger.warn('not in PARSING state, ignoring FRAG_PARSING_DATA event');
}
}
}, {
@ -1946,7 +1936,7 @@ var MSEMediaController = (function () {
}
}, {
key: 'onError',
value: function onError(event, data) {
value: function onError(data) {
switch (data.details) {
case _errors.ErrorDetails.FRAG_LOAD_ERROR:
case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT:
@ -1971,7 +1961,7 @@ var MSEMediaController = (function () {
_utilsLogger.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.hls.trigger(_events2['default'].ERROR, data);
this.state = State.ERROR;
}
}
@ -2037,14 +2027,14 @@ var MSEMediaController = (function () {
// playhead moving or media not playing
jumpThreshold = 0;
} else {
_utilsLogger.logger.trace('playback seems stuck');
_utilsLogger.logger.log('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)
// no buffer available @ currentTime, check if next buffer is close (more than 5ms diff but within a config.maxSeekHole second range)
var nextBufferStart = bufferInfo.nextStart,
delta = nextBufferStart - currentTime;
if (nextBufferStart && delta < 0.3 && delta > 0.005 && !media.seeking) {
if (nextBufferStart && delta < this.config.maxSeekHole && delta > 0.005 && !media.seeking) {
// next buffer is close ! adjust currentTime to nextBufferStart
// this will ensure effective video decoding
_utilsLogger.logger.log('adjust currentTime from ' + currentTime + ' to ' + nextBufferStart);
@ -2145,12 +2135,12 @@ var MSEMediaController = (function () {
}]);
return MSEMediaController;
})();
})(_eventHandler2['default']);
exports['default'] = MSEMediaController;
module.exports = exports['default'];
},{"../demux/demuxer":13,"../errors":17,"../events":18,"../helper/level-helper":19,"../utils/binary-search":27,"../utils/logger":28}],6:[function(require,module,exports){
},{"../demux/demuxer":13,"../errors":17,"../event-handler":18,"../events":19,"../helper/level-helper":20,"../utils/binary-search":28,"../utils/logger":29}],6:[function(require,module,exports){
/*
*
* This file contains an adaptation of the AES decryption algorithm
@ -2675,7 +2665,7 @@ var Decrypter = (function () {
exports['default'] = Decrypter;
module.exports = exports['default'];
},{"../errors":17,"../utils/logger":28,"./aes128-decrypter":7}],9:[function(require,module,exports){
},{"../errors":17,"../utils/logger":29,"./aes128-decrypter":7}],9:[function(require,module,exports){
/**
* AAC demuxer
*/
@ -2804,7 +2794,7 @@ var AACDemuxer = (function () {
exports['default'] = AACDemuxer;
module.exports = exports['default'];
},{"../demux/id3":15,"../utils/logger":28,"./adts":10}],10:[function(require,module,exports){
},{"../demux/id3":15,"../utils/logger":29,"./adts":10}],10:[function(require,module,exports){
/**
* ADTS parser helper
*/
@ -2952,7 +2942,7 @@ var ADTS = (function () {
exports['default'] = ADTS;
module.exports = exports['default'];
},{"../errors":17,"../utils/logger":28}],11:[function(require,module,exports){
},{"../errors":17,"../utils/logger":29}],11:[function(require,module,exports){
/* inline demuxer.
* probe fragments and instantiate appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...)
*/
@ -3024,7 +3014,7 @@ var DemuxerInline = (function () {
exports['default'] = DemuxerInline;
module.exports = exports['default'];
},{"../demux/aacdemuxer":9,"../demux/tsdemuxer":16,"../errors":17,"../events":18}],12:[function(require,module,exports){
},{"../demux/aacdemuxer":9,"../demux/tsdemuxer":16,"../errors":17,"../events":19}],12:[function(require,module,exports){
/* demuxer web worker.
* - listen to worker message, and trigger DemuxerInline upon reception of Fragments.
* - provides MP4 Boxes back to main thread using [transferable objects](https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast) in order to minimize message passing overhead.
@ -3131,7 +3121,7 @@ var DemuxerWorker = function DemuxerWorker(self) {
exports['default'] = DemuxerWorker;
module.exports = exports['default'];
},{"../demux/demuxer-inline":11,"../events":18,"../remux/mp4-remuxer":25,"events":1}],13:[function(require,module,exports){
},{"../demux/demuxer-inline":11,"../events":19,"../remux/mp4-remuxer":26,"events":1}],13:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
@ -3281,7 +3271,7 @@ var Demuxer = (function () {
exports['default'] = Demuxer;
module.exports = exports['default'];
},{"../crypt/decrypter":8,"../demux/demuxer-inline":11,"../demux/demuxer-worker":12,"../events":18,"../remux/mp4-remuxer":25,"../utils/logger":28,"webworkify":2}],14:[function(require,module,exports){
},{"../crypt/decrypter":8,"../demux/demuxer-inline":11,"../demux/demuxer-worker":12,"../events":19,"../remux/mp4-remuxer":26,"../utils/logger":29,"webworkify":2}],14:[function(require,module,exports){
/**
* Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
*/
@ -3620,7 +3610,7 @@ var ExpGolomb = (function () {
exports['default'] = ExpGolomb;
module.exports = exports['default'];
},{"../utils/logger":28}],15:[function(require,module,exports){
},{"../utils/logger":29}],15:[function(require,module,exports){
/**
* ID3 parser
*/
@ -3774,7 +3764,7 @@ var ID3 = (function () {
exports['default'] = ID3;
module.exports = exports['default'];
},{"../utils/logger":28}],16:[function(require,module,exports){
},{"../utils/logger":29}],16:[function(require,module,exports){
/**
* highly optimized TS demuxer:
* parse PAT, PMT
@ -4402,7 +4392,7 @@ var TSDemuxer = (function () {
exports['default'] = TSDemuxer;
module.exports = exports['default'];
},{"../errors":17,"../events":18,"../utils/logger":28,"./adts":10,"./exp-golomb":14}],17:[function(require,module,exports){
},{"../errors":17,"../events":19,"../utils/logger":29,"./adts":10,"./exp-golomb":14}],17:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
@ -4453,12 +4443,105 @@ var ErrorDetails = {
exports.ErrorDetails = ErrorDetails;
},{}],18:[function(require,module,exports){
/*
*
* All objects in the event handling chain should inherit from this class
*
*/
//import {logger} from './utils/logger';
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports['default'] = {
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'); } }
var EventHandler = (function () {
function EventHandler(hls) {
_classCallCheck(this, EventHandler);
this.hls = hls;
this.onEvent = this.onEvent.bind(this);
for (var _len = arguments.length, events = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
events[_key - 1] = arguments[_key];
}
this.handledEvents = events;
this.useGenericHandler = true;
this.registerListeners();
}
_createClass(EventHandler, [{
key: 'destroy',
value: function destroy() {
this.unregisterListeners();
}
}, {
key: 'isEventHandler',
value: function isEventHandler() {
return typeof this.handledEvents === 'object' && this.handledEvents.length && typeof this.onEvent === 'function';
}
}, {
key: 'registerListeners',
value: function registerListeners() {
if (this.isEventHandler()) {
this.handledEvents.forEach((function (event) {
if (event === 'hlsEventGeneric') {
throw new Error('Forbidden event name: ' + event);
}
this.hls.on(event, this.onEvent);
}).bind(this));
}
}
}, {
key: 'unregisterListeners',
value: function unregisterListeners() {
if (this.isEventHandler()) {
this.handledEvents.forEach((function (event) {
this.hls.off(event, this.onEvent);
}).bind(this));
}
}
/*
* arguments: event (string), data (any)
*/
}, {
key: 'onEvent',
value: function onEvent(event, data) {
this.onEventGeneric(event, data);
}
}, {
key: 'onEventGeneric',
value: function onEventGeneric(event, data) {
var eventToFunction = function eventToFunction(event, data) {
var funcName = 'on' + event.replace('hls', '');
if (typeof this[funcName] !== 'function') {
throw new Error('Event ' + event + ' has no generic handler in this ' + this.constructor.name + ' class (tried ' + funcName + ')');
}
return this[funcName].bind(this, data);
};
eventToFunction.call(this, event, data).call();
}
}]);
return EventHandler;
})();
exports['default'] = EventHandler;
module.exports = exports['default'];
},{}],19:[function(require,module,exports){
'use strict';
module.exports = {
// fired before MediaSource is attaching to media element - data: { media }
MEDIA_ATTACHING: 'hlsMediaAttaching',
// fired when MediaSource has been succesfully attached to media element - data: { }
@ -4480,7 +4563,7 @@ exports['default'] = {
// fired when a level's details have been updated based on previous details, after it has been loaded. - data: { details : levelDetails object, level : id of updated level }
LEVEL_UPDATED: 'hlsLevelUpdated',
// fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment }
LEVEL_PTS_UPDATED: 'hlsPTSUpdated',
LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated',
// fired when a level switch is requested - data: { level : id of new level }
LEVEL_SWITCH: 'hlsLevelSwitch',
// fired when a fragment loading starts - data: { frag : fragment object}
@ -4494,7 +4577,7 @@ exports['default'] = {
// fired when Init Segment has been extracted from fragment - data: { moov : moov MP4 box, codecs : codecs found while parsing fragment}
FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
// fired when parsing id3 is completed - data: { samples : [ id3 samples pes ] }
FRAG_PARSING_METADATA: 'hlsFraParsingMetadata',
FRAG_PARSING_METADATA: 'hlsFragParsingMetadata',
// fired when moof/mdat have been extracted from fragment - data: { moof : moof MP4 box, mdat : mdat MP4 box}
FRAG_PARSING_DATA: 'hlsFragParsingData',
// fired when fragment parsing is completed - data: undefined
@ -4504,7 +4587,7 @@ exports['default'] = {
// fired when fragment matching with current media position is changing - data : { frag : fragment object }
FRAG_CHANGED: 'hlsFragChanged',
// Identifier for a FPS drop event - data: {curentDropped, currentDecoded, totalDroppedFrames}
FPS_DROP: 'hlsFPSDrop',
FPS_DROP: 'hlsFpsDrop',
// Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data}
ERROR: 'hlsError',
// fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example
@ -4514,9 +4597,8 @@ exports['default'] = {
// fired when a decrypt key loading is completed - data: { frag : fragment object, payload : key payload, stats : { trequest, tfirst, tload, length}}
KEY_LOADED: 'hlsKeyLoaded'
};
module.exports = exports['default'];
},{}],19:[function(require,module,exports){
},{}],20:[function(require,module,exports){
/**
* Level Helper class, providing methods dealing with playlist sliding and drift
*/
@ -4662,7 +4744,7 @@ var LevelHelper = (function () {
exports['default'] = LevelHelper;
module.exports = exports['default'];
},{"../utils/logger":28}],20:[function(require,module,exports){
},{"../utils/logger":29}],21:[function(require,module,exports){
/**
* HLS interface
*/
@ -4750,6 +4832,8 @@ var Hls = (function () {
debug: false,
maxBufferLength: 30,
maxBufferSize: 60 * 1000 * 1000,
maxBufferHole: 0.3,
maxSeekHole: 2,
liveSyncDurationCount: 3,
liveMaxLatencyDurationCount: Infinity,
maxMaxBufferLength: 600,
@ -5016,7 +5100,7 @@ var Hls = (function () {
exports['default'] = Hls;
module.exports = exports['default'];
},{"./controller/abr-controller":3,"./controller/level-controller":4,"./controller/mse-media-controller":5,"./errors":17,"./events":18,"./loader/fragment-loader":21,"./loader/key-loader":22,"./loader/playlist-loader":23,"./utils/logger":28,"./utils/xhr-loader":30,"events":1}],21:[function(require,module,exports){
},{"./controller/abr-controller":3,"./controller/level-controller":4,"./controller/mse-media-controller":5,"./errors":17,"./events":19,"./loader/fragment-loader":22,"./loader/key-loader":23,"./loader/playlist-loader":24,"./utils/logger":29,"./utils/xhr-loader":31,"events":1}],22:[function(require,module,exports){
/*
* Fragment Loader
*/
@ -5029,23 +5113,31 @@ Object.defineProperty(exports, '__esModule', {
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; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
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'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _events = require('../events');
var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _errors = require('../errors');
var FragmentLoader = (function () {
var FragmentLoader = (function (_EventHandler) {
_inherits(FragmentLoader, _EventHandler);
function FragmentLoader(hls) {
_classCallCheck(this, FragmentLoader);
this.hls = hls;
this.onfl = this.onFragLoading.bind(this);
hls.on(_events2['default'].FRAG_LOADING, this.onfl);
_get(Object.getPrototypeOf(FragmentLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].FRAG_LOADING);
}
_createClass(FragmentLoader, [{
@ -5055,11 +5147,11 @@ var FragmentLoader = (function () {
this.loader.destroy();
this.loader = null;
}
this.hls.off(_events2['default'].FRAG_LOADING, this.onfl);
_eventHandler2['default'].prototype.destroy.call(this);
}
}, {
key: 'onFragLoading',
value: function onFragLoading(event, data) {
value: function onFragLoading(data) {
var frag = data.frag;
this.frag = frag;
this.frag.loaded = 0;
@ -5097,12 +5189,12 @@ var FragmentLoader = (function () {
}]);
return FragmentLoader;
})();
})(_eventHandler2['default']);
exports['default'] = FragmentLoader;
module.exports = exports['default'];
},{"../errors":17,"../events":18}],22:[function(require,module,exports){
},{"../errors":17,"../event-handler":18,"../events":19}],23:[function(require,module,exports){
/*
* Decrypt key Loader
*/
@ -5115,25 +5207,33 @@ Object.defineProperty(exports, '__esModule', {
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; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
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'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _events = require('../events');
var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _errors = require('../errors');
var KeyLoader = (function () {
var KeyLoader = (function (_EventHandler) {
_inherits(KeyLoader, _EventHandler);
function KeyLoader(hls) {
_classCallCheck(this, KeyLoader);
this.hls = hls;
_get(Object.getPrototypeOf(KeyLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].KEY_LOADING);
this.decryptkey = null;
this.decrypturl = null;
this.ondkl = this.onDecryptKeyLoading.bind(this);
hls.on(_events2['default'].KEY_LOADING, this.ondkl);
}
_createClass(KeyLoader, [{
@ -5143,11 +5243,11 @@ var KeyLoader = (function () {
this.loader.destroy();
this.loader = null;
}
this.hls.off(_events2['default'].KEY_LOADING, this.ondkl);
_eventHandler2['default'].prototype.destroy.call(this);
}
}, {
key: 'onDecryptKeyLoading',
value: function onDecryptKeyLoading(event, data) {
key: 'onKeyLoading',
value: function onKeyLoading(data) {
var frag = this.frag = data.frag,
decryptdata = frag.decryptdata,
uri = decryptdata.uri;
@ -5191,12 +5291,12 @@ var KeyLoader = (function () {
}]);
return KeyLoader;
})();
})(_eventHandler2['default']);
exports['default'] = KeyLoader;
module.exports = exports['default'];
},{"../errors":17,"../events":18}],23:[function(require,module,exports){
},{"../errors":17,"../event-handler":18,"../events":19}],24:[function(require,module,exports){
/**
* Playlist Loader
*/
@ -5209,14 +5309,22 @@ Object.defineProperty(exports, '__esModule', {
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; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
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'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _events = require('../events');
var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _errors = require('../errors');
var _utilsUrl = require('../utils/url');
@ -5229,15 +5337,13 @@ var _utilsAttrList2 = _interopRequireDefault(_utilsAttrList);
//import {logger} from '../utils/logger';
var PlaylistLoader = (function () {
var PlaylistLoader = (function (_EventHandler) {
_inherits(PlaylistLoader, _EventHandler);
function PlaylistLoader(hls) {
_classCallCheck(this, PlaylistLoader);
this.hls = hls;
this.onml = this.onManifestLoading.bind(this);
this.onll = this.onLevelLoading.bind(this);
hls.on(_events2['default'].MANIFEST_LOADING, this.onml);
hls.on(_events2['default'].LEVEL_LOADING, this.onll);
_get(Object.getPrototypeOf(PlaylistLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].MANIFEST_LOADING, _events2['default'].LEVEL_LOADING);
}
_createClass(PlaylistLoader, [{
@ -5248,17 +5354,16 @@ var PlaylistLoader = (function () {
this.loader = null;
}
this.url = this.id = null;
this.hls.off(_events2['default'].MANIFEST_LOADING, this.onml);
this.hls.off(_events2['default'].LEVEL_LOADING, this.onll);
_eventHandler2['default'].prototype.destroy.call(this);
}
}, {
key: 'onManifestLoading',
value: function onManifestLoading(event, data) {
value: function onManifestLoading(data) {
this.load(data.url, null);
}
}, {
key: 'onLevelLoading',
value: function onLevelLoading(event, data) {
value: function onLevelLoading(data) {
this.load(data.url, data.level, data.id);
}
}, {
@ -5525,12 +5630,12 @@ var PlaylistLoader = (function () {
}]);
return PlaylistLoader;
})();
})(_eventHandler2['default']);
exports['default'] = PlaylistLoader;
module.exports = exports['default'];
},{"../errors":17,"../events":18,"../utils/attr-list":26,"../utils/url":29}],24:[function(require,module,exports){
},{"../errors":17,"../event-handler":18,"../events":19,"../utils/attr-list":27,"../utils/url":30}],25:[function(require,module,exports){
/**
* Generate MP4 Box
*/
@ -6035,7 +6140,7 @@ var MP4 = (function () {
exports['default'] = MP4;
module.exports = exports['default'];
},{}],25:[function(require,module,exports){
},{}],26:[function(require,module,exports){
/**
* fMP4 remuxer
*/
@ -6194,6 +6299,7 @@ var MP4Remuxer = (function () {
dts,
ptsnorm,
dtsnorm,
flags,
samples = [];
/* concatenate the video data and construct the mdat in place
(need 8 more bytes to fill length and mpdat type) */
@ -6217,7 +6323,7 @@ var MP4Remuxer = (function () {
dts = avcSample.dts - this._initDTS;
// ensure DTS is not bigger than PTS
dts = Math.min(pts, dts);
//logger.log(`Video/PTS/DTS:${pts}/${dts}`);
//logger.log(`Video/PTS/DTS:${Math.round(pts/90)}/${Math.round(dts/90)}`);
// if not first AVC sample of video track, normalize PTS/DTS with previous sample value
// and ensure that sample duration is positive
if (lastDTS !== undefined) {
@ -6267,13 +6373,14 @@ var MP4Remuxer = (function () {
degradPrio: 0
}
};
flags = mp4Sample.flags;
if (avcSample.key === true) {
// the current sample is a key frame
mp4Sample.flags.dependsOn = 2;
mp4Sample.flags.isNonSync = 0;
flags.dependsOn = 2;
flags.isNonSync = 0;
} else {
mp4Sample.flags.dependsOn = 1;
mp4Sample.flags.isNonSync = 1;
flags.dependsOn = 1;
flags.isNonSync = 1;
}
samples.push(mp4Sample);
lastDTS = dtsnorm;
@ -6288,7 +6395,7 @@ var MP4Remuxer = (function () {
track.len = 0;
track.nbNalu = 0;
if (samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
var flags = samples[0].flags;
flags = samples[0].flags;
// chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue
// https://code.google.com/p/chromium/issues/detail?id=229412
flags.dependsOn = 2;
@ -6344,7 +6451,7 @@ var MP4Remuxer = (function () {
unit = aacSample.unit;
pts = aacSample.pts - this._initDTS;
dts = aacSample.dts - this._initDTS;
//logger.log(`Audio/PTS:${aacSample.pts.toFixed(0)}`);
//logger.log(`Audio/PTS:${Math.round(pts/90)}`);
// if not first sample
if (lastDTS !== undefined) {
ptsnorm = this._PTSNormalize(pts, lastDTS);
@ -6490,7 +6597,7 @@ var MP4Remuxer = (function () {
exports['default'] = MP4Remuxer;
module.exports = exports['default'];
},{"../errors":17,"../events":18,"../remux/mp4-generator":24,"../utils/logger":28}],26:[function(require,module,exports){
},{"../errors":17,"../events":19,"../remux/mp4-generator":25,"../utils/logger":29}],27:[function(require,module,exports){
// adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js
'use strict';
@ -6598,7 +6705,7 @@ var AttrList = (function () {
exports['default'] = AttrList;
module.exports = exports['default'];
},{}],27:[function(require,module,exports){
},{}],28:[function(require,module,exports){
"use strict";
var BinarySearch = {
@ -6643,7 +6750,7 @@ var BinarySearch = {
module.exports = BinarySearch;
},{}],28:[function(require,module,exports){
},{}],29:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
@ -6725,7 +6832,7 @@ exports.enableLogs = enableLogs;
var logger = exportedLogger;
exports.logger = logger;
},{}],29:[function(require,module,exports){
},{}],30:[function(require,module,exports){
'use strict';
var URLHelper = {
@ -6806,7 +6913,7 @@ var URLHelper = {
module.exports = URLHelper;
},{}],30:[function(require,module,exports){
},{}],31:[function(require,module,exports){
/**
* XHR based logger
*/
@ -6877,7 +6984,7 @@ var XhrLoader = (function () {
key: 'loadInternal',
value: function loadInternal() {
var xhr = this.loader = new XMLHttpRequest();
xhr.onreadystatechange = this.statechange.bind(this);
xhr.onloadend = this.loadend.bind(this);
xhr.onprogress = this.loadprogress.bind(this);
xhr.open('GET', this.url, true);
@ -6893,14 +7000,13 @@ var XhrLoader = (function () {
xhr.send();
}
}, {
key: 'statechange',
value: function statechange(event) {
key: 'loadend',
value: function loadend(event) {
var xhr = event.currentTarget,
status = xhr.status,
stats = this.stats;
// don't proceed if xhr has been aborted
// 4 = Response from server has been completely loaded.
if (!stats.aborted && xhr.readyState === 4) {
if (!stats.aborted) {
// http status between 200 to 299 are all successful
if (status >= 200 && status < 300) {
window.clearTimeout(this.timeoutHandle);
@ -6949,6 +7055,6 @@ var XhrLoader = (function () {
exports['default'] = XhrLoader;
module.exports = exports['default'];
},{"../utils/logger":28}]},{},[20])(20)
},{"../utils/logger":29}]},{},[21])(21)
});
//# 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.4.5",
"version": "0.4.6",
"description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js",
"authors": "Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>",

View file

@ -3,23 +3,22 @@
*/
import Event from '../events';
import EventHandler from '../event-handler';
class AbrController {
class AbrController extends EventHandler {
constructor(hls) {
this.hls = hls;
super(hls, Event.FRAG_LOAD_PROGRESS);
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);
EventHandler.prototype.destroy.call(this);
}
onFragmentLoadProgress(event, data) {
onFragLoadProgress(data) {
var stats = data.stats;
if (stats.aborted === undefined) {
this.lastfetchduration = (performance.now() - stats.trequest) / 1000;

View file

@ -3,35 +3,29 @@
*/
import Event from '../events';
import EventHandler from '../event-handler';
import {logger} from '../utils/logger';
import {ErrorTypes, ErrorDetails} from '../errors';
class LevelController {
class LevelController extends EventHandler {
constructor(hls) {
this.hls = hls;
this.onml = this.onManifestLoaded.bind(this);
this.onll = this.onLevelLoaded.bind(this);
this.onerr = this.onError.bind(this);
super(hls,
Event.MANIFEST_LOADED,
Event.LEVEL_LOADED,
Event.ERROR);
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) {
onManifestLoaded(data) {
var levels0 = [], levels = [], bitrateStart, i, bitrateSet = {}, videoCodecFound = false, audioCodecFound = false, hls = this.hls;
// regroup redundant level together
@ -166,7 +160,7 @@ class LevelController {
this._startLevel = newLevel;
}
onError(event, data) {
onError(data) {
if(data.fatal) {
return;
}
@ -224,7 +218,7 @@ class LevelController {
}
}
onLevelLoaded(event, data) {
onLevelLoaded(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

View file

@ -4,6 +4,7 @@
import Demuxer from '../demux/demuxer';
import Event from '../events';
import EventHandler from '../event-handler';
import {logger} from '../utils/logger';
import BinarySearch from '../utils/binary-search';
import LevelHelper from '../helper/level-helper';
@ -23,39 +24,31 @@ const State = {
BUFFER_FLUSHING : 8
};
class MSEMediaController {
class MSEMediaController extends EventHandler {
constructor(hls) {
super(hls, Event.MEDIA_ATTACHING,
Event.MEDIA_DETACHING,
Event.MANIFEST_PARSED,
Event.LEVEL_LOADED,
Event.KEY_LOADED,
Event.FRAG_LOADED,
Event.FRAG_PARSING_INIT_SEGMENT,
Event.FRAG_PARSING_DATA,
Event.FRAG_PARSED,
Event.ERROR);
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);
// internal listeners
this.onmediaatt0 = this.onMediaAttaching.bind(this);
this.onmediadet0 = this.onMediaDetaching.bind(this);
this.onmp = this.onManifestParsed.bind(this);
this.onll = this.onLevelLoaded.bind(this);
this.onfl = this.onFragLoaded.bind(this);
this.onkl = this.onKeyLoaded.bind(this);
this.onis = this.onInitSegment.bind(this);
this.onfpg = this.onFragParsing.bind(this);
this.onfp = this.onFragParsed.bind(this);
this.onerr = this.onError.bind(this);
this.ontick = this.tick.bind(this);
hls.on(Event.MEDIA_ATTACHING, this.onmediaatt0);
hls.on(Event.MEDIA_DETACHING, this.onmediadet0);
hls.on(Event.MANIFEST_PARSED, this.onmp);
}
destroy() {
this.stop();
var hls = this.hls;
hls.off(Event.MEDIA_ATTACHING, this.onmediaatt0);
hls.off(Event.MEDIA_DETACHING, this.onmediadet0);
hls.off(Event.MANIFEST_PARSED, this.onmp);
EventHandler.prototype.destroy.call(this);
this.state = State.IDLE;
}
@ -87,13 +80,6 @@ class MSEMediaController {
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);
hls.on(Event.FRAG_PARSED, this.onfp);
hls.on(Event.ERROR, this.onerr);
hls.on(Event.LEVEL_LOADED, this.onll);
hls.on(Event.KEY_LOADED, this.onkl);
}
stop() {
@ -128,14 +114,6 @@ class MSEMediaController {
this.demuxer.destroy();
this.demuxer = null;
}
var hls = this.hls;
hls.off(Event.FRAG_LOADED, this.onfl);
hls.off(Event.FRAG_PARSED, this.onfp);
hls.off(Event.FRAG_PARSING_DATA, this.onfpg);
hls.off(Event.LEVEL_LOADED, this.onll);
hls.off(Event.KEY_LOADED, this.onkl);
hls.off(Event.FRAG_PARSING_INIT_SEGMENT, this.onis);
hls.off(Event.ERROR, this.onerr);
}
tick() {
@ -189,7 +167,7 @@ class MSEMediaController {
// we are not at playback start, get next load level from level Controller
level = hls.nextLoadLevel;
}
var bufferInfo = this.bufferInfo(pos,0.3),
var bufferInfo = this.bufferInfo(pos,this.config.maxBufferHole),
bufferLen = bufferInfo.len,
bufferEnd = bufferInfo.end,
fragPrevious = this.fragPrevious,
@ -208,7 +186,9 @@ class MSEMediaController {
this.level = level;
levelDetails = this.levels[level].details;
// if level info not retrieved yet, switch state and wait for level retrieval
if (typeof levelDetails === 'undefined') {
// if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load
// a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist)
if (typeof levelDetails === 'undefined' || levelDetails.live && this.levelLastLoaded !== level) {
this.state = State.WAITING_LEVEL;
break;
}
@ -364,7 +344,7 @@ class MSEMediaController {
}
pos = v.currentTime;
var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate;
var bufferStarvationDelay = this.bufferInfo(pos,0.3).end - pos;
var bufferStarvationDelay = this.bufferInfo(pos,this.config.maxBufferHole).end - pos;
var fragLevelNextLoadedDelay = frag.duration * this.levels[hls.nextLoadLevel].bitrate / (8 * loadRate); //bps/Bps
/* if we have less than 2 frag duration in buffer and if frag loaded delay is greater than buffer starvation delay
... and also bigger than duration needed to load fragment at next level ...*/
@ -545,6 +525,7 @@ class MSEMediaController {
bufferLen = bufferEnd - pos;
} else if ((pos + maxHoleDuration) < start) {
bufferStartNext = start;
break;
}
}
return {len: bufferLen, start: bufferStart, end: bufferEnd, nextStart : bufferStartNext};
@ -797,7 +778,7 @@ class MSEMediaController {
}
}
onMediaAttaching(event, data) {
onMediaAttaching(data) {
var media = this.media = data.media;
// setup the media source
var ms = this.mediaSource = new MediaSource();
@ -870,7 +851,7 @@ class MSEMediaController {
if (this.state === State.FRAG_LOADING) {
// check if currently loaded fragment is inside buffer.
//if outside, cancel fragment loading, otherwise do nothing
if (this.bufferInfo(this.media.currentTime,0.3).len === 0) {
if (this.bufferInfo(this.media.currentTime,this.config.maxBufferHole).len === 0) {
logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
var fragCurrent = this.fragCurrent;
if (fragCurrent) {
@ -919,7 +900,7 @@ class MSEMediaController {
}
onManifestParsed(event, data) {
onManifestParsed(data) {
var aac = false, heaac = false, codecs;
data.levels.forEach(level => {
// detect if we have different kind of audio codecs used amongst playlists
@ -945,13 +926,14 @@ class MSEMediaController {
}
}
onLevelLoaded(event,data) {
onLevelLoaded(data) {
var newDetails = data.details,
newLevelId = data.level,
curLevel = this.levels[newLevelId],
duration = newDetails.totalduration;
logger.log(`level ${newLevelId} loaded [${newDetails.startSN},${newDetails.endSN}],duration:${duration}`);
this.levelLastLoaded = newLevelId;
if (newDetails.live) {
var curDetails = curLevel.details;
@ -998,7 +980,7 @@ class MSEMediaController {
}
}
onFragLoaded(event, data) {
onFragLoaded(data) {
var fragCurrent = this.fragCurrent;
if (this.state === State.FRAG_LOADING &&
fragCurrent &&
@ -1039,7 +1021,7 @@ class MSEMediaController {
this.fragLoadError = 0;
}
onInitSegment(event, data) {
onFragParsingInitSegment(data) {
if (this.state === State.PARSING) {
// 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
@ -1098,7 +1080,7 @@ class MSEMediaController {
}
}
onFragParsing(event, data) {
onFragParsingData(data) {
if (this.state === State.PARSING) {
this.tparse2 = Date.now();
var level = this.levels[this.level],
@ -1115,7 +1097,7 @@ class MSEMediaController {
//trigger handler right now
this.tick();
} else {
logger.warn(`not in PARSING state, discarding ${event}`);
logger.warn(`not in PARSING state, ignoring FRAG_PARSING_DATA event`);
}
}
@ -1128,7 +1110,7 @@ class MSEMediaController {
}
}
onError(event, data) {
onError(data) {
switch(data.details) {
case ErrorDetails.FRAG_LOAD_ERROR:
case ErrorDetails.FRAG_LOAD_TIMEOUT:
@ -1153,7 +1135,7 @@ class MSEMediaController {
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.hls.trigger(Event.ERROR, data);
this.state = State.ERROR;
}
}
@ -1216,14 +1198,14 @@ _checkBuffer() {
// playhead moving or media not playing
jumpThreshold = 0;
} else {
logger.trace('playback seems stuck');
logger.log('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)
// no buffer available @ currentTime, check if next buffer is close (more than 5ms diff but within a config.maxSeekHole second range)
var nextBufferStart = bufferInfo.nextStart, delta = nextBufferStart-currentTime;
if(nextBufferStart &&
(delta < 0.3) &&
(delta < this.config.maxSeekHole) &&
(delta > 0.005) &&
!media.seeking) {
// next buffer is close ! adjust currentTime to nextBufferStart

View file

@ -0,0 +1,66 @@
/*
*
* All objects in the event handling chain should inherit from this class
*
*/
//import {logger} from './utils/logger';
class EventHandler {
constructor(hls, ...events) {
this.hls = hls;
this.onEvent = this.onEvent.bind(this);
this.handledEvents = events;
this.useGenericHandler = true;
this.registerListeners();
}
destroy() {
this.unregisterListeners();
}
isEventHandler() {
return typeof this.handledEvents === 'object' && this.handledEvents.length && typeof this.onEvent === 'function';
}
registerListeners() {
if (this.isEventHandler()) {
this.handledEvents.forEach(function(event) {
if (event === 'hlsEventGeneric') {
throw new Error('Forbidden event name: ' + event);
}
this.hls.on(event, this.onEvent);
}.bind(this));
}
}
unregisterListeners() {
if (this.isEventHandler()) {
this.handledEvents.forEach(function(event) {
this.hls.off(event, this.onEvent);
}.bind(this));
}
}
/*
* arguments: event (string), data (any)
*/
onEvent(event, data) {
this.onEventGeneric(event, data);
}
onEventGeneric(event, data) {
var eventToFunction = function(event, data) {
var funcName = 'on' + event.replace('hls', '');
if (typeof this[funcName] !== 'function') {
throw new Error(`Event ${event} has no generic handler in this ${this.constructor.name} class (tried ${funcName})`);
}
return this[funcName].bind(this, data);
};
eventToFunction.call(this, event, data).call();
}
}
export default EventHandler;

View file

@ -1,4 +1,4 @@
export default {
module.exports = {
// fired before MediaSource is attaching to media element - data: { media }
MEDIA_ATTACHING: 'hlsMediaAttaching',
// fired when MediaSource has been succesfully attached to media element - data: { }
@ -20,7 +20,7 @@ export default {
// fired when a level's details have been updated based on previous details, after it has been loaded. - data: { details : levelDetails object, level : id of updated level }
LEVEL_UPDATED: 'hlsLevelUpdated',
// fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment }
LEVEL_PTS_UPDATED: 'hlsPTSUpdated',
LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated',
// fired when a level switch is requested - data: { level : id of new level }
LEVEL_SWITCH: 'hlsLevelSwitch',
// fired when a fragment loading starts - data: { frag : fragment object}
@ -34,7 +34,7 @@ export default {
// fired when Init Segment has been extracted from fragment - data: { moov : moov MP4 box, codecs : codecs found while parsing fragment}
FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
// fired when parsing id3 is completed - data: { samples : [ id3 samples pes ] }
FRAG_PARSING_METADATA: 'hlsFraParsingMetadata',
FRAG_PARSING_METADATA: 'hlsFragParsingMetadata',
// fired when moof/mdat have been extracted from fragment - data: { moof : moof MP4 box, mdat : mdat MP4 box}
FRAG_PARSING_DATA: 'hlsFragParsingData',
// fired when fragment parsing is completed - data: undefined
@ -44,7 +44,7 @@ export default {
// fired when fragment matching with current media position is changing - data : { frag : fragment object }
FRAG_CHANGED: 'hlsFragChanged',
// Identifier for a FPS drop event - data: {curentDropped, currentDecoded, totalDroppedFrames}
FPS_DROP: 'hlsFPSDrop',
FPS_DROP: 'hlsFpsDrop',
// Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data}
ERROR: 'hlsError',
// fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example

View file

@ -41,6 +41,8 @@ class Hls {
debug: false,
maxBufferLength: 30,
maxBufferSize: 60 * 1000 * 1000,
maxBufferHole: 0.3,
maxSeekHole: 2,
liveSyncDurationCount:3,
liveMaxLatencyDurationCount: Infinity,
maxMaxBufferLength: 600,

View file

@ -3,14 +3,13 @@
*/
import Event from '../events';
import EventHandler from '../event-handler';
import {ErrorTypes, ErrorDetails} from '../errors';
class FragmentLoader {
class FragmentLoader extends EventHandler {
constructor(hls) {
this.hls = hls;
this.onfl = this.onFragLoading.bind(this);
hls.on(Event.FRAG_LOADING, this.onfl);
super(hls, Event.FRAG_LOADING);
}
destroy() {
@ -18,10 +17,10 @@ class FragmentLoader {
this.loader.destroy();
this.loader = null;
}
this.hls.off(Event.FRAG_LOADING, this.onfl);
EventHandler.prototype.destroy.call(this);
}
onFragLoading(event, data) {
onFragLoading(data) {
var frag = data.frag;
this.frag = frag;
this.frag.loaded = 0;

View file

@ -3,16 +3,15 @@
*/
import Event from '../events';
import EventHandler from '../event-handler';
import {ErrorTypes, ErrorDetails} from '../errors';
class KeyLoader {
class KeyLoader extends EventHandler {
constructor(hls) {
this.hls = hls;
super(hls, Event.KEY_LOADING);
this.decryptkey = null;
this.decrypturl = null;
this.ondkl = this.onDecryptKeyLoading.bind(this);
hls.on(Event.KEY_LOADING, this.ondkl);
}
destroy() {
@ -20,10 +19,10 @@ class KeyLoader {
this.loader.destroy();
this.loader = null;
}
this.hls.off(Event.KEY_LOADING, this.ondkl);
EventHandler.prototype.destroy.call(this);
}
onDecryptKeyLoading(event, data) {
onKeyLoading(data) {
var frag = this.frag = data.frag,
decryptdata = frag.decryptdata,
uri = decryptdata.uri;

View file

@ -3,19 +3,18 @@
*/
import Event from '../events';
import EventHandler from '../event-handler';
import {ErrorTypes, ErrorDetails} from '../errors';
import URLHelper from '../utils/url';
import AttrList from '../utils/attr-list';
//import {logger} from '../utils/logger';
class PlaylistLoader {
class PlaylistLoader extends EventHandler {
constructor(hls) {
this.hls = hls;
this.onml = this.onManifestLoading.bind(this);
this.onll = this.onLevelLoading.bind(this);
hls.on(Event.MANIFEST_LOADING, this.onml);
hls.on(Event.LEVEL_LOADING, this.onll);
super(hls,
Event.MANIFEST_LOADING,
Event.LEVEL_LOADING);
}
destroy() {
@ -24,15 +23,14 @@ class PlaylistLoader {
this.loader = null;
}
this.url = this.id = null;
this.hls.off(Event.MANIFEST_LOADING, this.onml);
this.hls.off(Event.LEVEL_LOADING, this.onll);
EventHandler.prototype.destroy.call(this);
}
onManifestLoading(event, data) {
onManifestLoading(data) {
this.load(data.url, null);
}
onLevelLoading(event, data) {
onLevelLoading(data) {
this.load(data.url, data.level, data.id);
}

View file

@ -129,6 +129,7 @@ class MP4Remuxer {
mdat, moof,
firstPTS, firstDTS, lastDTS,
pts, dts, ptsnorm, dtsnorm,
flags,
samples = [];
/* concatenate the video data and construct the mdat in place
(need 8 more bytes to fill length and mpdat type) */
@ -152,7 +153,7 @@ class MP4Remuxer {
dts = avcSample.dts - this._initDTS;
// ensure DTS is not bigger than PTS
dts = Math.min(pts,dts);
//logger.log(`Video/PTS/DTS:${pts}/${dts}`);
//logger.log(`Video/PTS/DTS:${Math.round(pts/90)}/${Math.round(dts/90)}`);
// if not first AVC sample of video track, normalize PTS/DTS with previous sample value
// and ensure that sample duration is positive
if (lastDTS !== undefined) {
@ -201,13 +202,14 @@ class MP4Remuxer {
degradPrio: 0
}
};
flags = mp4Sample.flags;
if (avcSample.key === true) {
// the current sample is a key frame
mp4Sample.flags.dependsOn = 2;
mp4Sample.flags.isNonSync = 0;
flags.dependsOn = 2;
flags.isNonSync = 0;
} else {
mp4Sample.flags.dependsOn = 1;
mp4Sample.flags.isNonSync = 1;
flags.dependsOn = 1;
flags.isNonSync = 1;
}
samples.push(mp4Sample);
lastDTS = dtsnorm;
@ -222,7 +224,7 @@ class MP4Remuxer {
track.len = 0;
track.nbNalu = 0;
if(samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
var flags = samples[0].flags;
flags = samples[0].flags;
// chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue
// https://code.google.com/p/chromium/issues/detail?id=229412
flags.dependsOn = 2;
@ -270,7 +272,7 @@ class MP4Remuxer {
unit = aacSample.unit;
pts = aacSample.pts - this._initDTS;
dts = aacSample.dts - this._initDTS;
//logger.log(`Audio/PTS:${aacSample.pts.toFixed(0)}`);
//logger.log(`Audio/PTS:${Math.round(pts/90)}`);
// if not first sample
if (lastDTS !== undefined) {
ptsnorm = this._PTSNormalize(pts, lastDTS);

View file

@ -49,7 +49,7 @@ class XhrLoader {
loadInternal() {
var xhr = this.loader = new XMLHttpRequest();
xhr.onreadystatechange = this.statechange.bind(this);
xhr.onloadend = this.loadend.bind(this);
xhr.onprogress = this.loadprogress.bind(this);
xhr.open('GET', this.url, true);
@ -65,13 +65,12 @@ class XhrLoader {
xhr.send();
}
statechange(event) {
loadend(event) {
var xhr = event.currentTarget,
status = xhr.status,
stats = this.stats;
// don't proceed if xhr has been aborted
// 4 = Response from server has been completely loaded.
if (!stats.aborted && xhr.readyState === 4) {
if (!stats.aborted) {
// http status between 200 to 299 are all successful
if (status >= 200 && status < 300) {
window.clearTimeout(this.timeoutHandle);

View file

@ -32,14 +32,14 @@
"iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0"
},
"ignore": [],
"homepage": "https://github.com/PolymerElements/paper-ripple",
"homepage": "https://github.com/polymerelements/paper-ripple",
"_release": "1.0.5",
"_resolution": {
"type": "version",
"tag": "v1.0.5",
"commit": "d72e7a9a8ab518b901ed18dde492df3b87a93be5"
},
"_source": "git://github.com/PolymerElements/paper-ripple.git",
"_source": "git://github.com/polymerelements/paper-ripple.git",
"_target": "^1.0.0",
"_originalSource": "PolymerElements/paper-ripple"
"_originalSource": "polymerelements/paper-ripple"
}