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": {}, "devDependencies": {},
"ignore": [], "ignore": [],
"version": "1.0.20", "version": "1.0.21",
"_release": "1.0.20", "_release": "1.0.21",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "1.0.20", "tag": "1.0.21",
"commit": "b88c3533403d7fc921adb381c81917c80d9c4e77" "commit": "dd73237b9d554d45a664e820042804c6d129d280"
}, },
"_source": "git://github.com/MediaBrowser/emby-webcomponents.git", "_source": "git://github.com/MediaBrowser/emby-webcomponents.git",
"_target": "~1.0.0", "_target": "~1.0.0",

View file

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

View file

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

View file

@ -186,6 +186,8 @@ configuration parameters could be provided to hls.js upon instantiation of Hls O
maxBufferLength : 30, maxBufferLength : 30,
maxMaxBufferLength : 600, maxMaxBufferLength : 600,
maxBufferSize : 60*1000*1000, maxBufferSize : 60*1000*1000,
maxBufferHole : 0.3,
maxSeekHole : 2,
liveSyncDurationCount : 3, liveSyncDurationCount : 3,
liveMaxLatencyDurationCount: 10, liveMaxLatencyDurationCount: 10,
enableWorker : true, 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. '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``` #### ```maxMaxBufferLength```
(default 600s) (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 - `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 } - 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 - `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 - `Hls.Events.KEY_LOADING` - fired when a decryption key loading starts
- data: { frag : fragment object} - data: { frag : fragment object}
- `Hls.Events.KEY_LOADED` - fired when a decryption key loading is completed - `Hls.Events.KEY_LOADED` - fired when a decryption key loading is completed

View file

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

View file

@ -718,14 +718,6 @@ function timeRangesToString(r) {
events.buffer.push(event); events.buffer.push(event);
refreshCanvas(); 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:" var log = "Duration:"
+ v.duration + "<br>" + v.duration + "<br>"
+ "Buffered:" + "Buffered:"
@ -733,11 +725,16 @@ function timeRangesToString(r) {
+ "Seekable:" + "Seekable:"
+ timeRangesToString(v.seekable) + "<br>" + timeRangesToString(v.seekable) + "<br>"
+ "Played:" + "Played:"
+ timeRangesToString(v.played) + "<br>" + timeRangesToString(v.played) + "<br>";
+ "Decoded Frames:"
+ decodedFrames + "<br>"
+ "Dropped Frames:" var videoPlaybackQuality = v.getVideoPlaybackQuality;
+ droppedFrames + "<br>"; 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); $("#buffered_log").html(log);
$("#HlsStats").text(JSON.stringify(sortObject(stats),null,"\t")); $("#HlsStats").text(JSON.stringify(sortObject(stats),null,"\t"));
ctx.fillStyle = "blue"; 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 _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 _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 _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 _events = require('../events');
var _events2 = _interopRequireDefault(_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) { function AbrController(hls) {
_classCallCheck(this, AbrController); _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.lastfetchlevel = 0;
this._autoLevelCapping = -1; this._autoLevelCapping = -1;
this._nextAutoLevel = -1; this._nextAutoLevel = -1;
this.onflp = this.onFragmentLoadProgress.bind(this);
hls.on(_events2['default'].FRAG_LOAD_PROGRESS, this.onflp);
} }
_createClass(AbrController, [{ _createClass(AbrController, [{
key: 'destroy', key: 'destroy',
value: function destroy() { value: function destroy() {
this.hls.off(_events2['default'].FRAG_LOAD_PROGRESS, this.onflp); _eventHandler2['default'].prototype.destroy.call(this);
} }
}, { }, {
key: 'onFragmentLoadProgress', key: 'onFragLoadProgress',
value: function onFragmentLoadProgress(event, data) { value: function onFragLoadProgress(data) {
var stats = data.stats; var stats = data.stats;
if (stats.aborted === undefined) { if (stats.aborted === undefined) {
this.lastfetchduration = (performance.now() - stats.trequest) / 1000; this.lastfetchduration = (performance.now() - stats.trequest) / 1000;
@ -466,12 +474,12 @@ var AbrController = (function () {
}]); }]);
return AbrController; return AbrController;
})(); })(_eventHandler2['default']);
exports['default'] = AbrController; exports['default'] = AbrController;
module.exports = exports['default']; module.exports = exports['default'];
},{"../events":18}],4:[function(require,module,exports){ },{"../event-handler":18,"../events":19}],4:[function(require,module,exports){
/* /*
* Level Controller * 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 _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 _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 _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 _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _utilsLogger = require('../utils/logger'); var _utilsLogger = require('../utils/logger');
var _errors = require('../errors'); var _errors = require('../errors');
var LevelController = (function () { var LevelController = (function (_EventHandler) {
_inherits(LevelController, _EventHandler);
function LevelController(hls) { function LevelController(hls) {
_classCallCheck(this, LevelController); _classCallCheck(this, LevelController);
this.hls = hls; _get(Object.getPrototypeOf(LevelController.prototype), 'constructor', this).call(this, hls, _events2['default'].MANIFEST_LOADED, _events2['default'].LEVEL_LOADED, _events2['default'].ERROR);
this.onml = this.onManifestLoaded.bind(this);
this.onll = this.onLevelLoaded.bind(this);
this.onerr = this.onError.bind(this);
this.ontick = this.tick.bind(this); 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; this._manualLevel = this._autoLevelCapping = -1;
} }
_createClass(LevelController, [{ _createClass(LevelController, [{
key: 'destroy', key: 'destroy',
value: function 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) { if (this.timer) {
clearInterval(this.timer); clearInterval(this.timer);
} }
@ -525,7 +533,7 @@ var LevelController = (function () {
} }
}, { }, {
key: 'onManifestLoaded', key: 'onManifestLoaded',
value: function onManifestLoaded(event, data) { value: function onManifestLoaded(data) {
var levels0 = [], var levels0 = [],
levels = [], levels = [],
bitrateStart, bitrateStart,
@ -626,7 +634,7 @@ var LevelController = (function () {
} }
}, { }, {
key: 'onError', key: 'onError',
value: function onError(event, data) { value: function onError(data) {
if (data.fatal) { if (data.fatal) {
return; return;
} }
@ -688,7 +696,7 @@ var LevelController = (function () {
} }
}, { }, {
key: 'onLevelLoaded', key: 'onLevelLoaded',
value: function onLevelLoaded(event, data) { value: function onLevelLoaded(data) {
// check if current playlist is a live playlist // check if current playlist is a live playlist
if (data.details.live && !this.timer) { if (data.details.live && !this.timer) {
// if live playlist we will have to reload it periodically // if live playlist we will have to reload it periodically
@ -769,12 +777,12 @@ var LevelController = (function () {
}]); }]);
return LevelController; return LevelController;
})(); })(_eventHandler2['default']);
exports['default'] = LevelController; exports['default'] = LevelController;
module.exports = exports['default']; 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 * 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 _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 _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 _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 _demuxDemuxer = require('../demux/demuxer');
var _demuxDemuxer2 = _interopRequireDefault(_demuxDemuxer); var _demuxDemuxer2 = _interopRequireDefault(_demuxDemuxer);
@ -799,6 +811,10 @@ var _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _utilsLogger = require('../utils/logger'); var _utilsLogger = require('../utils/logger');
var _utilsBinarySearch = require('../utils/binary-search'); var _utilsBinarySearch = require('../utils/binary-search');
@ -825,42 +841,27 @@ var State = {
BUFFER_FLUSHING: 8 BUFFER_FLUSHING: 8
}; };
var MSEMediaController = (function () { var MSEMediaController = (function (_EventHandler) {
_inherits(MSEMediaController, _EventHandler);
function MSEMediaController(hls) { function MSEMediaController(hls) {
_classCallCheck(this, MSEMediaController); _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.config = hls.config;
this.audioCodecSwap = false; this.audioCodecSwap = false;
this.hls = hls;
this.ticks = 0; this.ticks = 0;
// Source Buffer listeners // Source Buffer listeners
this.onsbue = this.onSBUpdateEnd.bind(this); this.onsbue = this.onSBUpdateEnd.bind(this);
this.onsbe = this.onSBUpdateError.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); 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, [{ _createClass(MSEMediaController, [{
key: 'destroy', key: 'destroy',
value: function destroy() { value: function destroy() {
this.stop(); this.stop();
var hls = this.hls; _eventHandler2['default'].prototype.destroy.call(this);
hls.off(_events2['default'].MEDIA_ATTACHING, this.onmediaatt0);
hls.off(_events2['default'].MEDIA_DETACHING, this.onmediadet0);
hls.off(_events2['default'].MANIFEST_PARSED, this.onmp);
this.state = State.IDLE; this.state = State.IDLE;
} }
}, { }, {
@ -894,13 +895,6 @@ var MSEMediaController = (function () {
this.timer = setInterval(this.ontick, 100); this.timer = setInterval(this.ontick, 100);
this.level = -1; this.level = -1;
this.fragLoadError = 0; 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', key: 'stop',
@ -935,14 +929,6 @@ var MSEMediaController = (function () {
this.demuxer.destroy(); this.demuxer.destroy();
this.demuxer = null; 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', key: 'tick',
@ -1001,7 +987,7 @@ var MSEMediaController = (function () {
// we are not at playback start, get next load level from level Controller // we are not at playback start, get next load level from level Controller
level = hls.nextLoadLevel; level = hls.nextLoadLevel;
} }
var bufferInfo = this.bufferInfo(pos, 0.3), var bufferInfo = this.bufferInfo(pos, this.config.maxBufferHole),
bufferLen = bufferInfo.len, bufferLen = bufferInfo.len,
bufferEnd = bufferInfo.end, bufferEnd = bufferInfo.end,
fragPrevious = this.fragPrevious, fragPrevious = this.fragPrevious,
@ -1020,7 +1006,9 @@ var MSEMediaController = (function () {
this.level = level; this.level = level;
levelDetails = this.levels[level].details; levelDetails = this.levels[level].details;
// if level info not retrieved yet, switch state and wait for level retrieval // 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; this.state = State.WAITING_LEVEL;
break; break;
} }
@ -1176,7 +1164,7 @@ var MSEMediaController = (function () {
} }
pos = v.currentTime; pos = v.currentTime;
var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate; var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate;
var bufferStarvationDelay = this.bufferInfo(pos, 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 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 /* 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 ...*/ ... and also bigger than duration needed to load fragment at next level ...*/
@ -1363,6 +1351,7 @@ var MSEMediaController = (function () {
bufferLen = bufferEnd - pos; bufferLen = bufferEnd - pos;
} else if (pos + maxHoleDuration < start) { } else if (pos + maxHoleDuration < start) {
bufferStartNext = start; bufferStartNext = start;
break;
} }
} }
return { len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext }; return { len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext };
@ -1605,7 +1594,7 @@ var MSEMediaController = (function () {
} }
}, { }, {
key: 'onMediaAttaching', key: 'onMediaAttaching',
value: function onMediaAttaching(event, data) { value: function onMediaAttaching(data) {
var media = this.media = data.media; var media = this.media = data.media;
// setup the media source // setup the media source
var ms = this.mediaSource = new MediaSource(); var ms = this.mediaSource = new MediaSource();
@ -1680,7 +1669,7 @@ var MSEMediaController = (function () {
if (this.state === State.FRAG_LOADING) { if (this.state === State.FRAG_LOADING) {
// check if currently loaded fragment is inside buffer. // check if currently loaded fragment is inside buffer.
//if outside, cancel fragment loading, otherwise do nothing //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'); _utilsLogger.logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
var fragCurrent = this.fragCurrent; var fragCurrent = this.fragCurrent;
if (fragCurrent) { if (fragCurrent) {
@ -1732,7 +1721,7 @@ var MSEMediaController = (function () {
} }
}, { }, {
key: 'onManifestParsed', key: 'onManifestParsed',
value: function onManifestParsed(event, data) { value: function onManifestParsed(data) {
var aac = false, var aac = false,
heaac = false, heaac = false,
codecs; codecs;
@ -1761,13 +1750,14 @@ var MSEMediaController = (function () {
} }
}, { }, {
key: 'onLevelLoaded', key: 'onLevelLoaded',
value: function onLevelLoaded(event, data) { value: function onLevelLoaded(data) {
var newDetails = data.details, var newDetails = data.details,
newLevelId = data.level, newLevelId = data.level,
curLevel = this.levels[newLevelId], curLevel = this.levels[newLevelId],
duration = newDetails.totalduration; duration = newDetails.totalduration;
_utilsLogger.logger.log('level ' + newLevelId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration); _utilsLogger.logger.log('level ' + newLevelId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration);
this.levelLastLoaded = newLevelId;
if (newDetails.live) { if (newDetails.live) {
var curDetails = curLevel.details; var curDetails = curLevel.details;
@ -1816,7 +1806,7 @@ var MSEMediaController = (function () {
} }
}, { }, {
key: 'onFragLoaded', key: 'onFragLoaded',
value: function onFragLoaded(event, data) { value: function onFragLoaded(data) {
var fragCurrent = this.fragCurrent; var fragCurrent = this.fragCurrent;
if (this.state === State.FRAG_LOADING && fragCurrent && data.frag.level === fragCurrent.level && data.frag.sn === fragCurrent.sn) { if (this.state === State.FRAG_LOADING && fragCurrent && data.frag.level === fragCurrent.level && data.frag.sn === fragCurrent.sn) {
if (this.fragBitrateTest === true) { if (this.fragBitrateTest === true) {
@ -1854,8 +1844,8 @@ var MSEMediaController = (function () {
this.fragLoadError = 0; this.fragLoadError = 0;
} }
}, { }, {
key: 'onInitSegment', key: 'onFragParsingInitSegment',
value: function onInitSegment(event, data) { value: function onFragParsingInitSegment(data) {
if (this.state === State.PARSING) { if (this.state === State.PARSING) {
// check if codecs have been explicitely defined in the master playlist for this level; // 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 // if yes use these ones instead of the ones parsed from the demux
@ -1913,8 +1903,8 @@ var MSEMediaController = (function () {
} }
} }
}, { }, {
key: 'onFragParsing', key: 'onFragParsingData',
value: function onFragParsing(event, data) { value: function onFragParsingData(data) {
if (this.state === State.PARSING) { if (this.state === State.PARSING) {
this.tparse2 = Date.now(); this.tparse2 = Date.now();
var level = this.levels[this.level], var level = this.levels[this.level],
@ -1931,7 +1921,7 @@ var MSEMediaController = (function () {
//trigger handler right now //trigger handler right now
this.tick(); this.tick();
} else { } 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', key: 'onError',
value: function onError(event, data) { value: function onError(data) {
switch (data.details) { switch (data.details) {
case _errors.ErrorDetails.FRAG_LOAD_ERROR: case _errors.ErrorDetails.FRAG_LOAD_ERROR:
case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT: 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 ...'); _utilsLogger.logger.error('mediaController: ' + data.details + ' reaches max retry, redispatch as fatal ...');
// redispatch same error but with fatal set to true // redispatch same error but with fatal set to true
data.fatal = true; data.fatal = true;
this.hls.trigger(event, data); this.hls.trigger(_events2['default'].ERROR, data);
this.state = State.ERROR; this.state = State.ERROR;
} }
} }
@ -2037,14 +2027,14 @@ var MSEMediaController = (function () {
// playhead moving or media not playing // playhead moving or media not playing
jumpThreshold = 0; jumpThreshold = 0;
} else { } 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 we are below threshold, try to jump if next buffer range is close
if (bufferInfo.len <= jumpThreshold) { 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, var nextBufferStart = bufferInfo.nextStart,
delta = nextBufferStart - currentTime; 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 // next buffer is close ! adjust currentTime to nextBufferStart
// this will ensure effective video decoding // this will ensure effective video decoding
_utilsLogger.logger.log('adjust currentTime from ' + currentTime + ' to ' + nextBufferStart); _utilsLogger.logger.log('adjust currentTime from ' + currentTime + ' to ' + nextBufferStart);
@ -2145,12 +2135,12 @@ var MSEMediaController = (function () {
}]); }]);
return MSEMediaController; return MSEMediaController;
})(); })(_eventHandler2['default']);
exports['default'] = MSEMediaController; exports['default'] = MSEMediaController;
module.exports = exports['default']; 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 * This file contains an adaptation of the AES decryption algorithm
@ -2675,7 +2665,7 @@ var Decrypter = (function () {
exports['default'] = Decrypter; exports['default'] = Decrypter;
module.exports = exports['default']; 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 * AAC demuxer
*/ */
@ -2804,7 +2794,7 @@ var AACDemuxer = (function () {
exports['default'] = AACDemuxer; exports['default'] = AACDemuxer;
module.exports = exports['default']; 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 * ADTS parser helper
*/ */
@ -2952,7 +2942,7 @@ var ADTS = (function () {
exports['default'] = ADTS; exports['default'] = ADTS;
module.exports = exports['default']; 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. /* inline demuxer.
* probe fragments and instantiate appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...) * probe fragments and instantiate appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...)
*/ */
@ -3024,7 +3014,7 @@ var DemuxerInline = (function () {
exports['default'] = DemuxerInline; exports['default'] = DemuxerInline;
module.exports = exports['default']; 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. /* demuxer web worker.
* - listen to worker message, and trigger DemuxerInline upon reception of Fragments. * - 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. * - 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; exports['default'] = DemuxerWorker;
module.exports = exports['default']; 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'; 'use strict';
Object.defineProperty(exports, '__esModule', { Object.defineProperty(exports, '__esModule', {
@ -3281,7 +3271,7 @@ var Demuxer = (function () {
exports['default'] = Demuxer; exports['default'] = Demuxer;
module.exports = exports['default']; 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. * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
*/ */
@ -3620,7 +3610,7 @@ var ExpGolomb = (function () {
exports['default'] = ExpGolomb; exports['default'] = ExpGolomb;
module.exports = exports['default']; module.exports = exports['default'];
},{"../utils/logger":28}],15:[function(require,module,exports){ },{"../utils/logger":29}],15:[function(require,module,exports){
/** /**
* ID3 parser * ID3 parser
*/ */
@ -3774,7 +3764,7 @@ var ID3 = (function () {
exports['default'] = ID3; exports['default'] = ID3;
module.exports = exports['default']; module.exports = exports['default'];
},{"../utils/logger":28}],16:[function(require,module,exports){ },{"../utils/logger":29}],16:[function(require,module,exports){
/** /**
* highly optimized TS demuxer: * highly optimized TS demuxer:
* parse PAT, PMT * parse PAT, PMT
@ -4402,7 +4392,7 @@ var TSDemuxer = (function () {
exports['default'] = TSDemuxer; exports['default'] = TSDemuxer;
module.exports = exports['default']; 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'; 'use strict';
Object.defineProperty(exports, '__esModule', { Object.defineProperty(exports, '__esModule', {
@ -4453,12 +4443,105 @@ var ErrorDetails = {
exports.ErrorDetails = ErrorDetails; exports.ErrorDetails = ErrorDetails;
},{}],18:[function(require,module,exports){ },{}],18:[function(require,module,exports){
/*
*
* All objects in the event handling chain should inherit from this class
*
*/
//import {logger} from './utils/logger';
'use strict'; 'use strict';
Object.defineProperty(exports, '__esModule', { Object.defineProperty(exports, '__esModule', {
value: true 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 } // fired before MediaSource is attaching to media element - data: { media }
MEDIA_ATTACHING: 'hlsMediaAttaching', MEDIA_ATTACHING: 'hlsMediaAttaching',
// fired when MediaSource has been succesfully attached to media element - data: { } // 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 } // 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', 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 } // 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 } // fired when a level switch is requested - data: { level : id of new level }
LEVEL_SWITCH: 'hlsLevelSwitch', LEVEL_SWITCH: 'hlsLevelSwitch',
// fired when a fragment loading starts - data: { frag : fragment object} // 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} // 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', FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
// fired when parsing id3 is completed - data: { samples : [ id3 samples pes ] } // 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} // fired when moof/mdat have been extracted from fragment - data: { moof : moof MP4 box, mdat : mdat MP4 box}
FRAG_PARSING_DATA: 'hlsFragParsingData', FRAG_PARSING_DATA: 'hlsFragParsingData',
// fired when fragment parsing is completed - data: undefined // 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 } // fired when fragment matching with current media position is changing - data : { frag : fragment object }
FRAG_CHANGED: 'hlsFragChanged', FRAG_CHANGED: 'hlsFragChanged',
// Identifier for a FPS drop event - data: {curentDropped, currentDecoded, totalDroppedFrames} // 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} // 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', 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 // 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}} // fired when a decrypt key loading is completed - data: { frag : fragment object, payload : key payload, stats : { trequest, tfirst, tload, length}}
KEY_LOADED: 'hlsKeyLoaded' 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 * Level Helper class, providing methods dealing with playlist sliding and drift
*/ */
@ -4662,7 +4744,7 @@ var LevelHelper = (function () {
exports['default'] = LevelHelper; exports['default'] = LevelHelper;
module.exports = exports['default']; module.exports = exports['default'];
},{"../utils/logger":28}],20:[function(require,module,exports){ },{"../utils/logger":29}],21:[function(require,module,exports){
/** /**
* HLS interface * HLS interface
*/ */
@ -4750,6 +4832,8 @@ var Hls = (function () {
debug: false, debug: false,
maxBufferLength: 30, maxBufferLength: 30,
maxBufferSize: 60 * 1000 * 1000, maxBufferSize: 60 * 1000 * 1000,
maxBufferHole: 0.3,
maxSeekHole: 2,
liveSyncDurationCount: 3, liveSyncDurationCount: 3,
liveMaxLatencyDurationCount: Infinity, liveMaxLatencyDurationCount: Infinity,
maxMaxBufferLength: 600, maxMaxBufferLength: 600,
@ -5016,7 +5100,7 @@ var Hls = (function () {
exports['default'] = Hls; exports['default'] = Hls;
module.exports = exports['default']; 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 * 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 _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 _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 _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 _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _errors = require('../errors'); var _errors = require('../errors');
var FragmentLoader = (function () { var FragmentLoader = (function (_EventHandler) {
_inherits(FragmentLoader, _EventHandler);
function FragmentLoader(hls) { function FragmentLoader(hls) {
_classCallCheck(this, FragmentLoader); _classCallCheck(this, FragmentLoader);
this.hls = hls; _get(Object.getPrototypeOf(FragmentLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].FRAG_LOADING);
this.onfl = this.onFragLoading.bind(this);
hls.on(_events2['default'].FRAG_LOADING, this.onfl);
} }
_createClass(FragmentLoader, [{ _createClass(FragmentLoader, [{
@ -5055,11 +5147,11 @@ var FragmentLoader = (function () {
this.loader.destroy(); this.loader.destroy();
this.loader = null; this.loader = null;
} }
this.hls.off(_events2['default'].FRAG_LOADING, this.onfl); _eventHandler2['default'].prototype.destroy.call(this);
} }
}, { }, {
key: 'onFragLoading', key: 'onFragLoading',
value: function onFragLoading(event, data) { value: function onFragLoading(data) {
var frag = data.frag; var frag = data.frag;
this.frag = frag; this.frag = frag;
this.frag.loaded = 0; this.frag.loaded = 0;
@ -5097,12 +5189,12 @@ var FragmentLoader = (function () {
}]); }]);
return FragmentLoader; return FragmentLoader;
})(); })(_eventHandler2['default']);
exports['default'] = FragmentLoader; exports['default'] = FragmentLoader;
module.exports = exports['default']; 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 * 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 _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 _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 _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 _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _errors = require('../errors'); var _errors = require('../errors');
var KeyLoader = (function () { var KeyLoader = (function (_EventHandler) {
_inherits(KeyLoader, _EventHandler);
function KeyLoader(hls) { function KeyLoader(hls) {
_classCallCheck(this, KeyLoader); _classCallCheck(this, KeyLoader);
this.hls = hls; _get(Object.getPrototypeOf(KeyLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].KEY_LOADING);
this.decryptkey = null; this.decryptkey = null;
this.decrypturl = null; this.decrypturl = null;
this.ondkl = this.onDecryptKeyLoading.bind(this);
hls.on(_events2['default'].KEY_LOADING, this.ondkl);
} }
_createClass(KeyLoader, [{ _createClass(KeyLoader, [{
@ -5143,11 +5243,11 @@ var KeyLoader = (function () {
this.loader.destroy(); this.loader.destroy();
this.loader = null; this.loader = null;
} }
this.hls.off(_events2['default'].KEY_LOADING, this.ondkl); _eventHandler2['default'].prototype.destroy.call(this);
} }
}, { }, {
key: 'onDecryptKeyLoading', key: 'onKeyLoading',
value: function onDecryptKeyLoading(event, data) { value: function onKeyLoading(data) {
var frag = this.frag = data.frag, var frag = this.frag = data.frag,
decryptdata = frag.decryptdata, decryptdata = frag.decryptdata,
uri = decryptdata.uri; uri = decryptdata.uri;
@ -5191,12 +5291,12 @@ var KeyLoader = (function () {
}]); }]);
return KeyLoader; return KeyLoader;
})(); })(_eventHandler2['default']);
exports['default'] = KeyLoader; exports['default'] = KeyLoader;
module.exports = exports['default']; 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 * 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 _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 _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 _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 _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _errors = require('../errors'); var _errors = require('../errors');
var _utilsUrl = require('../utils/url'); var _utilsUrl = require('../utils/url');
@ -5229,15 +5337,13 @@ var _utilsAttrList2 = _interopRequireDefault(_utilsAttrList);
//import {logger} from '../utils/logger'; //import {logger} from '../utils/logger';
var PlaylistLoader = (function () { var PlaylistLoader = (function (_EventHandler) {
_inherits(PlaylistLoader, _EventHandler);
function PlaylistLoader(hls) { function PlaylistLoader(hls) {
_classCallCheck(this, PlaylistLoader); _classCallCheck(this, PlaylistLoader);
this.hls = hls; _get(Object.getPrototypeOf(PlaylistLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].MANIFEST_LOADING, _events2['default'].LEVEL_LOADING);
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);
} }
_createClass(PlaylistLoader, [{ _createClass(PlaylistLoader, [{
@ -5248,17 +5354,16 @@ var PlaylistLoader = (function () {
this.loader = null; this.loader = null;
} }
this.url = this.id = null; this.url = this.id = null;
this.hls.off(_events2['default'].MANIFEST_LOADING, this.onml); _eventHandler2['default'].prototype.destroy.call(this);
this.hls.off(_events2['default'].LEVEL_LOADING, this.onll);
} }
}, { }, {
key: 'onManifestLoading', key: 'onManifestLoading',
value: function onManifestLoading(event, data) { value: function onManifestLoading(data) {
this.load(data.url, null); this.load(data.url, null);
} }
}, { }, {
key: 'onLevelLoading', key: 'onLevelLoading',
value: function onLevelLoading(event, data) { value: function onLevelLoading(data) {
this.load(data.url, data.level, data.id); this.load(data.url, data.level, data.id);
} }
}, { }, {
@ -5525,12 +5630,12 @@ var PlaylistLoader = (function () {
}]); }]);
return PlaylistLoader; return PlaylistLoader;
})(); })(_eventHandler2['default']);
exports['default'] = PlaylistLoader; exports['default'] = PlaylistLoader;
module.exports = exports['default']; 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 * Generate MP4 Box
*/ */
@ -6035,7 +6140,7 @@ var MP4 = (function () {
exports['default'] = MP4; exports['default'] = MP4;
module.exports = exports['default']; module.exports = exports['default'];
},{}],25:[function(require,module,exports){ },{}],26:[function(require,module,exports){
/** /**
* fMP4 remuxer * fMP4 remuxer
*/ */
@ -6194,6 +6299,7 @@ var MP4Remuxer = (function () {
dts, dts,
ptsnorm, ptsnorm,
dtsnorm, dtsnorm,
flags,
samples = []; samples = [];
/* concatenate the video data and construct the mdat in place /* concatenate the video data and construct the mdat in place
(need 8 more bytes to fill length and mpdat type) */ (need 8 more bytes to fill length and mpdat type) */
@ -6217,7 +6323,7 @@ var MP4Remuxer = (function () {
dts = avcSample.dts - this._initDTS; dts = avcSample.dts - this._initDTS;
// ensure DTS is not bigger than PTS // ensure DTS is not bigger than PTS
dts = Math.min(pts, dts); 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 // if not first AVC sample of video track, normalize PTS/DTS with previous sample value
// and ensure that sample duration is positive // and ensure that sample duration is positive
if (lastDTS !== undefined) { if (lastDTS !== undefined) {
@ -6267,13 +6373,14 @@ var MP4Remuxer = (function () {
degradPrio: 0 degradPrio: 0
} }
}; };
flags = mp4Sample.flags;
if (avcSample.key === true) { if (avcSample.key === true) {
// the current sample is a key frame // the current sample is a key frame
mp4Sample.flags.dependsOn = 2; flags.dependsOn = 2;
mp4Sample.flags.isNonSync = 0; flags.isNonSync = 0;
} else { } else {
mp4Sample.flags.dependsOn = 1; flags.dependsOn = 1;
mp4Sample.flags.isNonSync = 1; flags.isNonSync = 1;
} }
samples.push(mp4Sample); samples.push(mp4Sample);
lastDTS = dtsnorm; lastDTS = dtsnorm;
@ -6288,7 +6395,7 @@ var MP4Remuxer = (function () {
track.len = 0; track.len = 0;
track.nbNalu = 0; track.nbNalu = 0;
if (samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { 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 // 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 // https://code.google.com/p/chromium/issues/detail?id=229412
flags.dependsOn = 2; flags.dependsOn = 2;
@ -6344,7 +6451,7 @@ var MP4Remuxer = (function () {
unit = aacSample.unit; unit = aacSample.unit;
pts = aacSample.pts - this._initDTS; pts = aacSample.pts - this._initDTS;
dts = aacSample.dts - 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 not first sample
if (lastDTS !== undefined) { if (lastDTS !== undefined) {
ptsnorm = this._PTSNormalize(pts, lastDTS); ptsnorm = this._PTSNormalize(pts, lastDTS);
@ -6490,7 +6597,7 @@ var MP4Remuxer = (function () {
exports['default'] = MP4Remuxer; exports['default'] = MP4Remuxer;
module.exports = exports['default']; 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 // adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js
'use strict'; 'use strict';
@ -6598,7 +6705,7 @@ var AttrList = (function () {
exports['default'] = AttrList; exports['default'] = AttrList;
module.exports = exports['default']; module.exports = exports['default'];
},{}],27:[function(require,module,exports){ },{}],28:[function(require,module,exports){
"use strict"; "use strict";
var BinarySearch = { var BinarySearch = {
@ -6643,7 +6750,7 @@ var BinarySearch = {
module.exports = BinarySearch; module.exports = BinarySearch;
},{}],28:[function(require,module,exports){ },{}],29:[function(require,module,exports){
'use strict'; 'use strict';
Object.defineProperty(exports, '__esModule', { Object.defineProperty(exports, '__esModule', {
@ -6725,7 +6832,7 @@ exports.enableLogs = enableLogs;
var logger = exportedLogger; var logger = exportedLogger;
exports.logger = logger; exports.logger = logger;
},{}],29:[function(require,module,exports){ },{}],30:[function(require,module,exports){
'use strict'; 'use strict';
var URLHelper = { var URLHelper = {
@ -6806,7 +6913,7 @@ var URLHelper = {
module.exports = URLHelper; module.exports = URLHelper;
},{}],30:[function(require,module,exports){ },{}],31:[function(require,module,exports){
/** /**
* XHR based logger * XHR based logger
*/ */
@ -6877,7 +6984,7 @@ var XhrLoader = (function () {
key: 'loadInternal', key: 'loadInternal',
value: function loadInternal() { value: function loadInternal() {
var xhr = this.loader = new XMLHttpRequest(); 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.onprogress = this.loadprogress.bind(this);
xhr.open('GET', this.url, true); xhr.open('GET', this.url, true);
@ -6893,14 +7000,13 @@ var XhrLoader = (function () {
xhr.send(); xhr.send();
} }
}, { }, {
key: 'statechange', key: 'loadend',
value: function statechange(event) { value: function loadend(event) {
var xhr = event.currentTarget, var xhr = event.currentTarget,
status = xhr.status, status = xhr.status,
stats = this.stats; stats = this.stats;
// don't proceed if xhr has been aborted // don't proceed if xhr has been aborted
// 4 = Response from server has been completely loaded. if (!stats.aborted) {
if (!stats.aborted && xhr.readyState === 4) {
// http status between 200 to 299 are all successful // http status between 200 to 299 are all successful
if (status >= 200 && status < 300) { if (status >= 200 && status < 300) {
window.clearTimeout(this.timeoutHandle); window.clearTimeout(this.timeoutHandle);
@ -6949,6 +7055,6 @@ var XhrLoader = (function () {
exports['default'] = XhrLoader; exports['default'] = XhrLoader;
module.exports = exports['default']; module.exports = exports['default'];
},{"../utils/logger":28}]},{},[20])(20) },{"../utils/logger":29}]},{},[21])(21)
}); });
//# sourceMappingURL=hls.js.map //# 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", "name": "hls.js",
"version": "0.4.5", "version": "0.4.6",
"description": "Media Source Extension - HLS library, by/for Dailymotion", "description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js", "homepage": "https://github.com/dailymotion/hls.js",
"authors": "Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>", "authors": "Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>",

View file

@ -3,23 +3,22 @@
*/ */
import Event from '../events'; import Event from '../events';
import EventHandler from '../event-handler';
class AbrController { class AbrController extends EventHandler {
constructor(hls) { constructor(hls) {
this.hls = hls; super(hls, Event.FRAG_LOAD_PROGRESS);
this.lastfetchlevel = 0; this.lastfetchlevel = 0;
this._autoLevelCapping = -1; this._autoLevelCapping = -1;
this._nextAutoLevel = -1; this._nextAutoLevel = -1;
this.onflp = this.onFragmentLoadProgress.bind(this);
hls.on(Event.FRAG_LOAD_PROGRESS, this.onflp);
} }
destroy() { destroy() {
this.hls.off(Event.FRAG_LOAD_PROGRESS, this.onflp); EventHandler.prototype.destroy.call(this);
} }
onFragmentLoadProgress(event, data) { onFragLoadProgress(data) {
var stats = data.stats; var stats = data.stats;
if (stats.aborted === undefined) { if (stats.aborted === undefined) {
this.lastfetchduration = (performance.now() - stats.trequest) / 1000; this.lastfetchduration = (performance.now() - stats.trequest) / 1000;

View file

@ -3,35 +3,29 @@
*/ */
import Event from '../events'; import Event from '../events';
import EventHandler from '../event-handler';
import {logger} from '../utils/logger'; import {logger} from '../utils/logger';
import {ErrorTypes, ErrorDetails} from '../errors'; import {ErrorTypes, ErrorDetails} from '../errors';
class LevelController { class LevelController extends EventHandler {
constructor(hls) { constructor(hls) {
this.hls = hls; super(hls,
this.onml = this.onManifestLoaded.bind(this); Event.MANIFEST_LOADED,
this.onll = this.onLevelLoaded.bind(this); Event.LEVEL_LOADED,
this.onerr = this.onError.bind(this); Event.ERROR);
this.ontick = this.tick.bind(this); this.ontick = this.tick.bind(this);
hls.on(Event.MANIFEST_LOADED, this.onml);
hls.on(Event.LEVEL_LOADED, this.onll);
hls.on(Event.ERROR, this.onerr);
this._manualLevel = this._autoLevelCapping = -1; this._manualLevel = this._autoLevelCapping = -1;
} }
destroy() { 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) { if (this.timer) {
clearInterval(this.timer); clearInterval(this.timer);
} }
this._manualLevel = -1; this._manualLevel = -1;
} }
onManifestLoaded(event, data) { onManifestLoaded(data) {
var levels0 = [], levels = [], bitrateStart, i, bitrateSet = {}, videoCodecFound = false, audioCodecFound = false, hls = this.hls; var levels0 = [], levels = [], bitrateStart, i, bitrateSet = {}, videoCodecFound = false, audioCodecFound = false, hls = this.hls;
// regroup redundant level together // regroup redundant level together
@ -166,7 +160,7 @@ class LevelController {
this._startLevel = newLevel; this._startLevel = newLevel;
} }
onError(event, data) { onError(data) {
if(data.fatal) { if(data.fatal) {
return; return;
} }
@ -224,7 +218,7 @@ class LevelController {
} }
} }
onLevelLoaded(event, data) { onLevelLoaded(data) {
// check if current playlist is a live playlist // check if current playlist is a live playlist
if (data.details.live && !this.timer) { if (data.details.live && !this.timer) {
// if live playlist we will have to reload it periodically // if live playlist we will have to reload it periodically

View file

@ -4,6 +4,7 @@
import Demuxer from '../demux/demuxer'; import Demuxer from '../demux/demuxer';
import Event from '../events'; import Event from '../events';
import EventHandler from '../event-handler';
import {logger} from '../utils/logger'; import {logger} from '../utils/logger';
import BinarySearch from '../utils/binary-search'; import BinarySearch from '../utils/binary-search';
import LevelHelper from '../helper/level-helper'; import LevelHelper from '../helper/level-helper';
@ -23,39 +24,31 @@ const State = {
BUFFER_FLUSHING : 8 BUFFER_FLUSHING : 8
}; };
class MSEMediaController { class MSEMediaController extends EventHandler {
constructor(hls) { 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.config = hls.config;
this.audioCodecSwap = false; this.audioCodecSwap = false;
this.hls = hls;
this.ticks = 0; this.ticks = 0;
// Source Buffer listeners // Source Buffer listeners
this.onsbue = this.onSBUpdateEnd.bind(this); this.onsbue = this.onSBUpdateEnd.bind(this);
this.onsbe = this.onSBUpdateError.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); 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() { destroy() {
this.stop(); this.stop();
var hls = this.hls; EventHandler.prototype.destroy.call(this);
hls.off(Event.MEDIA_ATTACHING, this.onmediaatt0);
hls.off(Event.MEDIA_DETACHING, this.onmediadet0);
hls.off(Event.MANIFEST_PARSED, this.onmp);
this.state = State.IDLE; this.state = State.IDLE;
} }
@ -87,13 +80,6 @@ class MSEMediaController {
this.timer = setInterval(this.ontick, 100); this.timer = setInterval(this.ontick, 100);
this.level = -1; this.level = -1;
this.fragLoadError = 0; 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() { stop() {
@ -128,14 +114,6 @@ class MSEMediaController {
this.demuxer.destroy(); this.demuxer.destroy();
this.demuxer = null; 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() { tick() {
@ -189,7 +167,7 @@ class MSEMediaController {
// we are not at playback start, get next load level from level Controller // we are not at playback start, get next load level from level Controller
level = hls.nextLoadLevel; level = hls.nextLoadLevel;
} }
var bufferInfo = this.bufferInfo(pos,0.3), var bufferInfo = this.bufferInfo(pos,this.config.maxBufferHole),
bufferLen = bufferInfo.len, bufferLen = bufferInfo.len,
bufferEnd = bufferInfo.end, bufferEnd = bufferInfo.end,
fragPrevious = this.fragPrevious, fragPrevious = this.fragPrevious,
@ -208,7 +186,9 @@ class MSEMediaController {
this.level = level; this.level = level;
levelDetails = this.levels[level].details; levelDetails = this.levels[level].details;
// if level info not retrieved yet, switch state and wait for level retrieval // 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; this.state = State.WAITING_LEVEL;
break; break;
} }
@ -364,7 +344,7 @@ class MSEMediaController {
} }
pos = v.currentTime; pos = v.currentTime;
var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate; var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate;
var bufferStarvationDelay = this.bufferInfo(pos,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 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 /* 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 ...*/ ... and also bigger than duration needed to load fragment at next level ...*/
@ -545,6 +525,7 @@ class MSEMediaController {
bufferLen = bufferEnd - pos; bufferLen = bufferEnd - pos;
} else if ((pos + maxHoleDuration) < start) { } else if ((pos + maxHoleDuration) < start) {
bufferStartNext = start; bufferStartNext = start;
break;
} }
} }
return {len: bufferLen, start: bufferStart, end: bufferEnd, nextStart : bufferStartNext}; 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; var media = this.media = data.media;
// setup the media source // setup the media source
var ms = this.mediaSource = new MediaSource(); var ms = this.mediaSource = new MediaSource();
@ -870,7 +851,7 @@ class MSEMediaController {
if (this.state === State.FRAG_LOADING) { if (this.state === State.FRAG_LOADING) {
// check if currently loaded fragment is inside buffer. // check if currently loaded fragment is inside buffer.
//if outside, cancel fragment loading, otherwise do nothing //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'); logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
var fragCurrent = this.fragCurrent; var fragCurrent = this.fragCurrent;
if (fragCurrent) { if (fragCurrent) {
@ -919,7 +900,7 @@ class MSEMediaController {
} }
onManifestParsed(event, data) { onManifestParsed(data) {
var aac = false, heaac = false, codecs; var aac = false, heaac = false, codecs;
data.levels.forEach(level => { data.levels.forEach(level => {
// detect if we have different kind of audio codecs used amongst playlists // 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, var newDetails = data.details,
newLevelId = data.level, newLevelId = data.level,
curLevel = this.levels[newLevelId], curLevel = this.levels[newLevelId],
duration = newDetails.totalduration; duration = newDetails.totalduration;
logger.log(`level ${newLevelId} loaded [${newDetails.startSN},${newDetails.endSN}],duration:${duration}`); logger.log(`level ${newLevelId} loaded [${newDetails.startSN},${newDetails.endSN}],duration:${duration}`);
this.levelLastLoaded = newLevelId;
if (newDetails.live) { if (newDetails.live) {
var curDetails = curLevel.details; var curDetails = curLevel.details;
@ -998,7 +980,7 @@ class MSEMediaController {
} }
} }
onFragLoaded(event, data) { onFragLoaded(data) {
var fragCurrent = this.fragCurrent; var fragCurrent = this.fragCurrent;
if (this.state === State.FRAG_LOADING && if (this.state === State.FRAG_LOADING &&
fragCurrent && fragCurrent &&
@ -1039,7 +1021,7 @@ class MSEMediaController {
this.fragLoadError = 0; this.fragLoadError = 0;
} }
onInitSegment(event, data) { onFragParsingInitSegment(data) {
if (this.state === State.PARSING) { if (this.state === State.PARSING) {
// check if codecs have been explicitely defined in the master playlist for this level; // 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 // 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) { if (this.state === State.PARSING) {
this.tparse2 = Date.now(); this.tparse2 = Date.now();
var level = this.levels[this.level], var level = this.levels[this.level],
@ -1115,7 +1097,7 @@ class MSEMediaController {
//trigger handler right now //trigger handler right now
this.tick(); this.tick();
} else { } 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) { switch(data.details) {
case ErrorDetails.FRAG_LOAD_ERROR: case ErrorDetails.FRAG_LOAD_ERROR:
case ErrorDetails.FRAG_LOAD_TIMEOUT: case ErrorDetails.FRAG_LOAD_TIMEOUT:
@ -1153,7 +1135,7 @@ class MSEMediaController {
logger.error(`mediaController: ${data.details} reaches max retry, redispatch as fatal ...`); logger.error(`mediaController: ${data.details} reaches max retry, redispatch as fatal ...`);
// redispatch same error but with fatal set to true // redispatch same error but with fatal set to true
data.fatal = true; data.fatal = true;
this.hls.trigger(event, data); this.hls.trigger(Event.ERROR, data);
this.state = State.ERROR; this.state = State.ERROR;
} }
} }
@ -1216,14 +1198,14 @@ _checkBuffer() {
// playhead moving or media not playing // playhead moving or media not playing
jumpThreshold = 0; jumpThreshold = 0;
} else { } 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 we are below threshold, try to jump if next buffer range is close
if(bufferInfo.len <= jumpThreshold) { 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; var nextBufferStart = bufferInfo.nextStart, delta = nextBufferStart-currentTime;
if(nextBufferStart && if(nextBufferStart &&
(delta < 0.3) && (delta < this.config.maxSeekHole) &&
(delta > 0.005) && (delta > 0.005) &&
!media.seeking) { !media.seeking) {
// next buffer is close ! adjust currentTime to nextBufferStart // 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 } // fired before MediaSource is attaching to media element - data: { media }
MEDIA_ATTACHING: 'hlsMediaAttaching', MEDIA_ATTACHING: 'hlsMediaAttaching',
// fired when MediaSource has been succesfully attached to media element - data: { } // 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 } // 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', 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 } // 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 } // fired when a level switch is requested - data: { level : id of new level }
LEVEL_SWITCH: 'hlsLevelSwitch', LEVEL_SWITCH: 'hlsLevelSwitch',
// fired when a fragment loading starts - data: { frag : fragment object} // 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} // 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', FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
// fired when parsing id3 is completed - data: { samples : [ id3 samples pes ] } // 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} // fired when moof/mdat have been extracted from fragment - data: { moof : moof MP4 box, mdat : mdat MP4 box}
FRAG_PARSING_DATA: 'hlsFragParsingData', FRAG_PARSING_DATA: 'hlsFragParsingData',
// fired when fragment parsing is completed - data: undefined // 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 } // fired when fragment matching with current media position is changing - data : { frag : fragment object }
FRAG_CHANGED: 'hlsFragChanged', FRAG_CHANGED: 'hlsFragChanged',
// Identifier for a FPS drop event - data: {curentDropped, currentDecoded, totalDroppedFrames} // 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} // 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', 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 // 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, debug: false,
maxBufferLength: 30, maxBufferLength: 30,
maxBufferSize: 60 * 1000 * 1000, maxBufferSize: 60 * 1000 * 1000,
maxBufferHole: 0.3,
maxSeekHole: 2,
liveSyncDurationCount:3, liveSyncDurationCount:3,
liveMaxLatencyDurationCount: Infinity, liveMaxLatencyDurationCount: Infinity,
maxMaxBufferLength: 600, maxMaxBufferLength: 600,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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