1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

fix merge conflicts

This commit is contained in:
Luke Pulverenti 2016-01-19 14:49:42 -05:00
parent bcfee41a57
commit d33230d361
97 changed files with 1205 additions and 811 deletions

View file

@ -16,12 +16,12 @@
},
"devDependencies": {},
"ignore": [],
"version": "1.0.25",
"_release": "1.0.25",
"version": "1.0.26",
"_release": "1.0.26",
"_resolution": {
"type": "version",
"tag": "1.0.25",
"commit": "f2e83b0e30527b5182ceb043d170ad7188368245"
"tag": "1.0.26",
"commit": "6fe8727397b13e87e3afaeee600f600269e0ee18"
},
"_source": "git://github.com/MediaBrowser/Emby.ApiClient.Javascript.git",
"_target": "~1.0.3",

View file

@ -420,6 +420,7 @@
saveUserInfoIntoCredentials(server, result.User);
credentialProvider.credentials(credentials);
apiClient.serverInfo(server);
afterConnected(apiClient, options);
onLocalUserSignIn(server, server.LastConnectionMode, result.User);
@ -748,44 +749,38 @@
console.log('Begin getConnectServers');
return new Promise(function (resolve, reject) {
if (!credentials.ConnectAccessToken || !credentials.ConnectUserId) {
return Promise.resolve([]);
}
if (!credentials.ConnectAccessToken || !credentials.ConnectUserId) {
resolve([]);
return;
var url = "https://connect.emby.media/service/servers?userId=" + credentials.ConnectUserId;
return ajax({
type: "GET",
url: url,
dataType: "json",
headers: {
"X-Application": appName + "/" + appVersion,
"X-Connect-UserToken": credentials.ConnectAccessToken
}
var url = "https://connect.emby.media/service/servers?userId=" + credentials.ConnectUserId;
ajax({
type: "GET",
url: url,
dataType: "json",
headers: {
"X-Application": appName + "/" + appVersion,
"X-Connect-UserToken": credentials.ConnectAccessToken
}
}).then(function (servers) {
servers = servers.map(function (i) {
return {
ExchangeToken: i.AccessKey,
ConnectServerId: i.Id,
Id: i.SystemId,
Name: i.Name,
RemoteAddress: i.Url,
LocalAddress: i.LocalAddress,
UserLinkType: (i.UserType || '').toLowerCase() == "guest" ? "Guest" : "LinkedUser"
};
});
resolve(servers);
}, function () {
resolve([]);
}).then(function (servers) {
return servers.map(function (i) {
return {
ExchangeToken: i.AccessKey,
ConnectServerId: i.Id,
Id: i.SystemId,
Name: i.Name,
RemoteAddress: i.Url,
LocalAddress: i.LocalAddress,
UserLinkType: (i.UserType || '').toLowerCase() == "guest" ? "Guest" : "LinkedUser"
};
});
}, function () {
return [];
});
}
@ -858,9 +853,8 @@
var info = {
Id: foundServer.Id,
LocalAddress: foundServer.Address,
LocalAddress: convertEndpointAddressToManualAddress(foundServer) || foundServer.Address,
Name: foundServer.Name,
ManualAddress: convertEndpointAddressToManualAddress(foundServer),
DateLastLocalConnection: new Date().getTime()
};
@ -1467,13 +1461,10 @@
self.getRegistrationInfo = function (feature, apiClient) {
if (isConnectUserSupporter()) {
return new Promise(function (resolve, reject) {
resolve({
Name: feature,
IsRegistered: true,
IsTrial: false
});
return Promise.resolve({
Name: feature,
IsRegistered: true,
IsTrial: false
});
}

View file

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

View file

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

View file

@ -0,0 +1,25 @@
define([], function () {
function loadImage(elem, url) {
if (elem.tagName !== "IMG") {
var tmp = new Image();
tmp.onload = function () {
elem.style.backgroundImage = "url('" + url + "')";
};
tmp.src = url;
} else {
elem.setAttribute("src", url);
}
return Promise.resolve(elem);
}
return {
loadImage: loadImage
};
});

View file

@ -0,0 +1,175 @@
define(['visibleinviewport', 'imageloader'], function (visibleinviewport, imageLoader) {
var thresholdX = screen.availWidth;
var thresholdY = screen.availHeight;
var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel');
function isVisible(elem) {
return visibleinviewport(elem, true, thresholdX, thresholdY);
}
function fillImage(elem) {
var source = elem.getAttribute('data-src');
if (source) {
imageLoader.loadImage(elem, source);
elem.setAttribute("data-src", '');
}
}
function cancelAll(tokens) {
for (var i = 0, length = tokens.length; i < length; i++) {
tokens[i] = true;
}
}
function unveilElements(images) {
if (!images.length) {
return;
}
var cancellationTokens = [];
function unveilInternal(tokenIndex) {
var remaining = [];
var anyFound = false;
var out = false;
// TODO: This out construct assumes left to right, top to bottom
for (var i = 0, length = images.length; i < length; i++) {
if (cancellationTokens[tokenIndex]) {
return;
}
var img = images[i];
if (!out && isVisible(img)) {
anyFound = true;
fillImage(img);
} else {
if (anyFound) {
out = true;
}
remaining.push(img);
}
}
images = remaining;
if (!images.length) {
document.removeEventListener('focus', unveil, true);
document.removeEventListener('scroll', unveil, true);
document.removeEventListener(wheelEvent, unveil, true);
window.removeEventListener('resize', unveil, true);
}
}
function unveil() {
cancelAll(cancellationTokens);
var index = cancellationTokens.length;
cancellationTokens.length++;
setTimeout(function () {
unveilInternal(index);
}, 1);
}
document.addEventListener('scroll', unveil, true);
document.addEventListener('focus', unveil, true);
document.addEventListener(wheelEvent, unveil, true);
window.addEventListener('resize', unveil, true);
unveil();
}
function fillImages(elems) {
for (var i = 0, length = elems.length; i < length; i++) {
var elem = elems[0];
var source = elem.getAttribute('data-src');
if (source) {
ImageStore.setImageInto(elem, source);
elem.setAttribute("data-src", '');
}
}
}
function lazyChildren(elem) {
unveilElements(elem.getElementsByClassName('lazy'));
}
function lazyImage(elem, url) {
elem.setAttribute('data-src', url);
fillImages([elem]);
}
function getPrimaryImageAspectRatio(items) {
var values = [];
for (var i = 0, length = items.length; i < length; i++) {
var ratio = items[i].PrimaryImageAspectRatio || 0;
if (!ratio) {
continue;
}
values[values.length] = ratio;
}
if (!values.length) {
return null;
}
// Use the median
values.sort(function (a, b) { return a - b; });
var half = Math.floor(values.length / 2);
var result;
if (values.length % 2)
result = values[half];
else
result = (values[half - 1] + values[half]) / 2.0;
// If really close to 2:3 (poster image), just return 2:3
var aspect2x3 = 2 / 3;
if (Math.abs(aspect2x3 - result) <= .15) {
return aspect2x3;
}
// If really close to 16:9 (episode image), just return 16:9
var aspect16x9 = 16 / 9;
if (Math.abs(aspect16x9 - result) <= .2) {
return aspect16x9;
}
// If really close to 1 (square image), just return 1
if (Math.abs(1 - result) <= .15) {
return 1;
}
// If really close to 4:3 (poster image), just return 2:3
var aspect4x3 = 4 / 3;
if (Math.abs(aspect4x3 - result) <= .15) {
return aspect4x3;
}
return result;
}
return {
lazyChildren: lazyChildren,
getPrimaryImageAspectRatio: getPrimaryImageAspectRatio
};
});

View file

@ -0,0 +1,164 @@
define(['cryptojs-md5'], function () {
function setImageIntoElement(elem, url) {
if (elem.tagName !== "IMG") {
elem.style.backgroundImage = "url('" + url + "')";
} else {
elem.setAttribute("src", url);
}
}
// Request Quota (only for File System API)
var requestedBytes = 1024 * 1024 * 1500;
var imageCacheDirectoryEntry;
var imageCacheFolder = 'images';
function createDir(rootDirEntry, folders, callback, errorCallback) {
// Throw out './' or '/' and move on to prevent something like '/foo/.//bar'.
if (folders[0] == '.' || folders[0] == '') {
folders = folders.slice(1);
}
rootDirEntry.getDirectory(folders[0], { create: true }, function (dirEntry) {
// Recursively add the new subfolder (if we still have another to create).
if (folders.length > 1) {
createDir(dirEntry, folders.slice(1), callback, errorCallback);
} else {
callback(dirEntry);
}
}, errorCallback);
}
navigator.webkitPersistentStorage.requestQuota(
requestedBytes, function (grantedBytes) {
var requestMethod = window.webkitRequestFileSystem || window.requestFileSystem;
requestMethod(PERSISTENT, grantedBytes, function (fs) {
fileSystem = fs;
createDir(fileSystem.root, imageCacheFolder.split('/'), function (dirEntry) {
imageCacheDirectoryEntry = dirEntry;
});
});
});
function getCacheKey(url) {
// Try to strip off the domain to share the cache between local and remote connections
var index = url.indexOf('://');
if (index != -1) {
url = url.substring(index + 3);
index = url.indexOf('/');
if (index != -1) {
url = url.substring(index + 1);
}
}
return CryptoJS.MD5(url).toString();
}
function downloadToFile(url, dir, filename, callback, errorCallback) {
console.log('Downloading ' + url);
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "arraybuffer";
xhr.onload = function (e) {
if (this.status == 200) {
writeData(dir, filename, this.getResponseHeader('Content-Type'), this.response, callback, errorCallback);
} else {
errorCallback();
}
}
xhr.send();
}
function writeData(dir, filename, fileType, data, callback, errorCallback) {
dir.getFile(filename, { create: true }, function (fileEntry) {
// Create a FileWriter object for our FileEntry (log.txt).
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function (e) {
callback(fileEntry);
};
fileWriter.onerror = errorCallback;
// Create a new Blob and write it to log.txt.
var blob = new Blob([data], { type: fileType });
fileWriter.write(blob);
}, errorCallback);
}, errorCallback);
}
function getImageUrl(originalUrl) {
return new Promise(function (resolve, reject) {
if (originalUrl.indexOf('tag=') != -1) {
originalUrl += "&accept=webp";
}
var key = getCacheKey(originalUrl);
var fileEntryCallback = function (fileEntry) {
resolve(fileEntry.toURL());
};
var errorCallback = function (e) {
console.log('Imagestore error: ' + e.name);
reject();
};
if (!fileSystem || !imageCacheDirectoryEntry) {
errorCallback('');
return;
}
var path = '/' + imageCacheFolder + "/" + key;
fileSystem.root.getFile(path, { create: false }, fileEntryCallback, function () {
downloadToFile(originalUrl, imageCacheDirectoryEntry, key, fileEntryCallback, errorCallback);
});
});
}
var fileSystem;
return {
loadImage: function (elem, url) {
return getImageUrl(url).then(function (localUrl) {
setImageIntoElement(elem, localUrl);
return elem;
}, function () {
setImageIntoElement(elem, url);
return elem;
});
}
};
});

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
{
"name": "hls.js",
"version": "0.4.5",
"version": "0.4.6",
"description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js",
"authors": "Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -29,14 +29,14 @@
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
},
"ignore": [],
"homepage": "https://github.com/polymerelements/iron-behaviors",
"homepage": "https://github.com/PolymerElements/iron-behaviors",
"_release": "1.0.12",
"_resolution": {
"type": "version",
"tag": "v1.0.12",
"commit": "657f526a2382a659cdf4e13be87ecc89261588a3"
},
"_source": "git://github.com/polymerelements/iron-behaviors.git",
"_source": "git://github.com/PolymerElements/iron-behaviors.git",
"_target": "^1.0.0",
"_originalSource": "polymerelements/iron-behaviors"
"_originalSource": "PolymerElements/iron-behaviors"
}

View file

@ -1,6 +1,6 @@
{
"name": "paper-input",
"version": "1.1.4",
"version": "1.1.5",
"description": "Material design text fields",
"authors": [
"The Polymer Authors"
@ -44,14 +44,14 @@
"iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
"paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
"test-fixture": "PolymerElements/test-fixture#^1.0.0",
"web-component-tester": "Polymer/web-component-tester#^3.4.0",
"web-component-tester": "^4.0.0",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
},
"_release": "1.1.4",
"_release": "1.1.5",
"_resolution": {
"type": "version",
"tag": "v1.1.4",
"commit": "8ca01ac3cafc61abd980d262875ffca0c79640fa"
"tag": "v1.1.5",
"commit": "0aa8318b5e026688f94c78c7673acabf5bad0f17"
},
"_source": "git://github.com/polymerelements/paper-input.git",
"_target": "^1.0.9",

View file

@ -1,6 +1,6 @@
{
"name": "paper-input",
"version": "1.1.4",
"version": "1.1.5",
"description": "Material design text fields",
"authors": [
"The Polymer Authors"
@ -44,7 +44,7 @@
"iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
"paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
"test-fixture": "PolymerElements/test-fixture#^1.0.0",
"web-component-tester": "Polymer/web-component-tester#^3.4.0",
"web-component-tester": "^4.0.0",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
}
}

View file

@ -32,7 +32,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
*/
/**
* The label for this input. Bind this to `<label>`'s content and `hidden` property, e.g.
* The label for this input. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* `<label>`'s content and `hidden` property, e.g.
* `<label hidden$="[[!label]]">[[label]]</label>` in your `template`
*/
label: {
@ -40,7 +42,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* The value for this input. Bind this to the `<input is="iron-input">`'s `bindValue`
* The value for this input. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* the `<input is="iron-input">`'s `bindValue`
* property, or the value property of your input that is `notify:true`.
*/
value: {
@ -49,8 +53,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Set to true to disable this input. Bind this to both the `<paper-input-container>`'s
* and the input's `disabled` property.
* Set to true to disable this input. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* both the `<paper-input-container>`'s and the input's `disabled` property.
*/
disabled: {
type: Boolean,
@ -58,9 +63,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Returns true if the value is invalid. Bind this to both the `<paper-input-container>`'s
* and the input's `invalid` property.
*
* Returns true if the value is invalid. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to both the
* `<paper-input-container>`'s and the input's `invalid` property.
*
* If `autoValidate` is true, the `invalid` attribute is managed automatically,
* which can clobber attempts to manage it manually.
*/
@ -71,48 +77,55 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Set to true to prevent the user from entering invalid input. Bind this to the
* `<input is="iron-input">`'s `preventInvalidInput` property.
* Set to true to prevent the user from entering invalid input. If you're
* using PaperInputBehavior to implement your own paper-input-like element,
* bind this to `<input is="iron-input">`'s `preventInvalidInput` property.
*/
preventInvalidInput: {
type: Boolean
},
/**
* Set this to specify the pattern allowed by `preventInvalidInput`. Bind this to the
* `<input is="iron-input">`'s `allowedPattern` property.
* Set this to specify the pattern allowed by `preventInvalidInput`. If
* you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `allowedPattern`
* property.
*/
allowedPattern: {
type: String
},
/**
* The type of the input. The supported types are `text`, `number` and `password`. Bind this
* to the `<input is="iron-input">`'s `type` property.
* The type of the input. The supported types are `text`, `number` and `password`.
* If you're using PaperInputBehavior to implement your own paper-input-like element,
* bind this to the `<input is="iron-input">`'s `type` property.
*/
type: {
type: String
},
/**
* The datalist of the input (if any). This should match the id of an existing `<datalist>`. Bind this
* to the `<input is="iron-input">`'s `list` property.
* The datalist of the input (if any). This should match the id of an existing `<datalist>`.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `list` property.
*/
list: {
type: String
},
/**
* A pattern to validate the `input` with. Bind this to the `<input is="iron-input">`'s
* `pattern` property.
* A pattern to validate the `input` with. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* the `<input is="iron-input">`'s `pattern` property.
*/
pattern: {
type: String
},
/**
* Set to true to mark the input as required. Bind this to the `<input is="iron-input">`'s
* `required` property.
* Set to true to mark the input as required. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* the `<input is="iron-input">`'s `required` property.
*/
required: {
type: Boolean,
@ -120,8 +133,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* The error message to display when the input is invalid. Bind this to the
* `<paper-input-error>`'s content, if using.
* The error message to display when the input is invalid. If you're using
* PaperInputBehavior to implement your own paper-input-like element,
* bind this to the `<paper-input-error>`'s content, if using.
*/
errorMessage: {
type: String
@ -136,8 +150,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Set to true to disable the floating label. Bind this to the `<paper-input-container>`'s
* `noLabelFloat` property.
* Set to true to disable the floating label. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* the `<paper-input-container>`'s `noLabelFloat` property.
*/
noLabelFloat: {
type: Boolean,
@ -145,8 +160,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Set to true to always float the label. Bind this to the `<paper-input-container>`'s
* `alwaysFloatLabel` property.
* Set to true to always float the label. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* the `<paper-input-container>`'s `alwaysFloatLabel` property.
*/
alwaysFloatLabel: {
type: Boolean,
@ -154,8 +170,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Set to true to auto-validate the input value. Bind this to the `<paper-input-container>`'s
* `autoValidate` property.
* Set to true to auto-validate the input value. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* the `<paper-input-container>`'s `autoValidate` property.
*/
autoValidate: {
type: Boolean,
@ -163,8 +180,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Name of the validator to use. Bind this to the `<input is="iron-input">`'s `validator`
* property.
* Name of the validator to use. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* the `<input is="iron-input">`'s `validator` property.
*/
validator: {
type: String
@ -173,7 +191,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// HTMLInputElement attributes for binding if needed
/**
* Bind this to the `<input is="iron-input">`'s `autocomplete` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `autocomplete` property.
*/
autocomplete: {
type: String,
@ -181,29 +200,34 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Bind this to the `<input is="iron-input">`'s `autofocus` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `autofocus` property.
*/
autofocus: {
type: Boolean
},
/**
* Bind this to the `<input is="iron-input">`'s `inputmode` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `inputmode` property.
*/
inputmode: {
type: String
},
/**
* Bind this to the `<input is="iron-input">`'s `minlength` property.
* The minimum length of the input value.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `minlength` property.
*/
minlength: {
type: Number
},
/**
* The maximum length of the input value. Bind this to the `<input is="iron-input">`'s
* `maxlength` property.
* The maximum length of the input value.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `maxlength` property.
*/
maxlength: {
type: Number
@ -211,7 +235,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/**
* The minimum (numeric or date-time) input value.
* Bind this to the `<input is="iron-input">`'s `min` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `min` property.
*/
min: {
type: String
@ -220,7 +245,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/**
* The maximum (numeric or date-time) input value.
* Can be a String (e.g. `"2000-1-1"`) or a Number (e.g. `2`).
* Bind this to the `<input is="iron-input">`'s `max` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `max` property.
*/
max: {
type: String
@ -228,14 +254,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/**
* Limits the numeric or date-time increments.
* Bind this to the `<input is="iron-input">`'s `step` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `step` property.
*/
step: {
type: String
},
/**
* Bind this to the `<input is="iron-input">`'s `name` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `name` property.
*/
name: {
type: String
@ -251,7 +279,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Bind this to the `<input is="iron-input">`'s `readonly` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `readonly` property.
*/
readonly: {
type: Boolean,
@ -259,7 +288,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Bind this to the `<input is="iron-input">`'s `size` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `size` property.
*/
size: {
type: Number
@ -268,7 +298,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// Nonstandard attributes for binding if needed
/**
* Bind this to the `<input is="iron-input">`'s `autocapitalize` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `autocapitalize` property.
*/
autocapitalize: {
type: String,
@ -276,7 +307,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Bind this to the `<input is="iron-input">`'s `autocorrect` property.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `autocorrect` property.
*/
autocorrect: {
type: String,
@ -284,28 +316,36 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Bind this to the `<input is="iron-input">`'s `autosave` property, used with type=search.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `autosave` property,
* used with type=search.
*/
autosave: {
type: String
},
/**
* Bind this to the `<input is="iron-input">`'s `results` property, used with type=search.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `results` property,
* used with type=search.
*/
results: {
type: Number
},
/**
* Bind this to the `<input is="iron-input">`'s `accept` property, used with type=file.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `accept` property,
* used with type=file.
*/
accept: {
type: String
},
/**
* Bind this to the `<input is="iron-input">`'s `multiple` property, used with type=file.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the`<input is="iron-input">`'s `multiple` property,
* used with type=file.
*/
multiple: {
type: Boolean
@ -354,8 +394,20 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
return this.inputElement;
},
registered: function() {
// These types have some default placeholder text; overlapping
// the label on top of it looks terrible. Auto-float the label in this case.
this._typesThatHaveText = ["date", "datetime", "datetime-local", "month",
"time", "week", "file"];
},
attached: function() {
this._updateAriaLabelledBy();
if (this.inputElement &&
this._typesThatHaveText.indexOf(this.inputElement.type) !== -1) {
this.alwaysFloatLabel = true;
}
},
_appendStringWithSpace: function(str, more) {

View file

@ -39,9 +39,10 @@ for `suffix`).
</paper-input>
A `paper-input` can use the native `type=search` or `type=file` features.
However, since we can't control the native styling of the input, in these cases
it's recommended to use a placeholder text, or `always-float-label`,
as to not overlap the native UI (search icon, file button, etc.).
However, since we can't control the native styling of the input (search icon,
file button, date placeholder, etc.), in these cases the label will be
automatically floated. The `placeholder` attribute can still be used for
additional informational text.
<paper-input label="search!" type="search"
placeholder="search for cats" autosave="test" results="5">
@ -100,6 +101,7 @@ style this element.
aria-labelledby$="[[_ariaLabelledBy]]"
aria-describedby$="[[_ariaDescribedBy]]"
disabled$="[[disabled]]"
title$="[[title]]"
bind-value="{{value}}"
invalid="{{invalid}}"
prevent-invalid-input="[[preventInvalidInput]]"

View file

@ -70,7 +70,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
</template>
</test-fixture>
<test-fixture id="required-char-counter">
<template>
<paper-input auto-validate char-counter required error-message="error"></paper-input>
@ -95,6 +94,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
</template>
</test-fixture>
<test-fixture id="date">
<template>
<paper-input label="foo" type="date"></paper-input>
</template>
</test-fixture>
<letters-only></letters-only>
<test-fixture id="validator">
@ -121,6 +126,18 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
assert.ok(floatingLabel);
});
test('special types autofloat the label', function() {
var input = fixture('date');
// Browsers that don't support special <input> types like `date` fallback
// to `text`, so make sure to only test if type is still preserved after
// the element is attached.
if (input.inputElement.type === "date") {
assert.equal(input.alwaysFloatLabel, true);
var floatingLabel = Polymer.dom(Polymer.dom(input.root).querySelector('paper-input-container').root).querySelector('.label-is-floating');
assert.ok(floatingLabel);
}
});
test('always-float-label attribute works without placeholder', function() {
var input = fixture('always-float-label');
var container = Polymer.dom(input.root).querySelector('paper-input-container');

View file

@ -24,14 +24,14 @@
"web-component-tester": "*"
},
"private": true,
"homepage": "https://github.com/polymer/polymer",
"homepage": "https://github.com/Polymer/polymer",
"_release": "1.2.3",
"_resolution": {
"type": "version",
"tag": "v1.2.3",
"commit": "aa535d1675342007cbf64dc9c66497cf74cbc616"
},
"_source": "git://github.com/polymer/polymer.git",
"_source": "git://github.com/Polymer/polymer.git",
"_target": "^1.0.0",
"_originalSource": "polymer/polymer"
"_originalSource": "Polymer/polymer"
}