mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
update web components
This commit is contained in:
parent
909402537a
commit
fcdd2e4f4b
114 changed files with 1611 additions and 1238 deletions
11
dashboard-ui/bower_components/hls.js/.bower.json
vendored
11
dashboard-ui/bower_components/hls.js/.bower.json
vendored
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "hls.js",
|
||||
"version": "0.3.11",
|
||||
"version": "0.3.15",
|
||||
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
||||
"homepage": "https://github.com/dailymotion/hls.js",
|
||||
"authors": [
|
||||
|
@ -15,14 +15,13 @@
|
|||
"test",
|
||||
"tests"
|
||||
],
|
||||
"_release": "0.3.11",
|
||||
"_release": "0.3.15",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.3.11",
|
||||
"commit": "4e698e4adc4e1e0fa80ef9c8334a5ef382bbe347"
|
||||
"tag": "v0.3.15",
|
||||
"commit": "d3ecf55b89063d7ba3bd70800d5839755b0c7e63"
|
||||
},
|
||||
"_source": "git://github.com/dailymotion/hls.js.git",
|
||||
"_target": "~0.3.11",
|
||||
"_originalSource": "dailymotion/hls.js",
|
||||
"_direct": true
|
||||
"_originalSource": "dailymotion/hls.js"
|
||||
}
|
4
dashboard-ui/bower_components/hls.js/API.md
vendored
4
dashboard-ui/bower_components/hls.js/API.md
vendored
|
@ -197,7 +197,7 @@ configuration parameters could be provided to hls.js upon instantiation of Hls O
|
|||
manifestLoadingRetryDelay : 500,
|
||||
fpsDroppedMonitoringPeriod : 5000,
|
||||
fpsDroppedMonitoringThreshold : 0.2,
|
||||
appendErrorMaxRetry : 200,
|
||||
appendErrorMaxRetry : 3,
|
||||
loader : customLoader,
|
||||
fLoader: customFragmentLoader,
|
||||
pLoader: customPlaylistLoader,
|
||||
|
@ -275,7 +275,7 @@ any I/O error will trigger retries every 500ms,1s,2s,4s,8s, ... capped to 64s (e
|
|||
|
||||
max nb of append retry
|
||||
#### ```appendErrorMaxRetry```
|
||||
(default 200)
|
||||
(default 3)
|
||||
|
||||
max number of sourceBuffer.appendBuffer() retry upon error.
|
||||
such error could happen in loop with UHD streams, when internal buffer is full. (Quota Exceeding Error will be triggered). in that case we need to wait for the browser to evict some data before being able to append buffer correctly.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "hls.js",
|
||||
"version": "0.3.11",
|
||||
"version": "0.3.15",
|
||||
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
||||
"homepage": "https://github.com/dailymotion/hls.js",
|
||||
"authors": [
|
||||
|
|
|
@ -72,8 +72,9 @@ header {
|
|||
<div id="customButtons"></div>
|
||||
<select id="streamSelect" class="innerControls"><option value="" selected>(Enter custom URL below)</option></select>
|
||||
<input id="streamURL" class="innerControls" type=text value=""/>
|
||||
<label class="innerControls"><input id="enableStream" type=checkbox checked/> Enable Streaming</label>
|
||||
<label class="innerControls"><input id="enableStreaming" type=checkbox checked/> Enable Streaming</label>
|
||||
<label class="innerControls"><input id="autoRecoverError" type=checkbox checked/> Auto-Recover Media Error</label>
|
||||
<label class="innerControls"><input id="enableWorker" type=checkbox checked/> Enable Worker</label>
|
||||
<div id="StreamPermalink" class="innerControls"></div>
|
||||
<div>
|
||||
<select id="videoSize" style="float:left">
|
||||
|
@ -201,21 +202,25 @@ $(document).ready(function() {
|
|||
$("#StatsDisplay").hide();
|
||||
$('#metricsButtonWindow').toggle(windowSliding);
|
||||
$('#metricsButtonFixed').toggle(!windowSliding);
|
||||
$('#enableStream').click(function() { enableStreaming = this.checked; loadStream($('#streamURL').val());});
|
||||
$('#autoRecoverError').prop( "checked", true );
|
||||
$('#autoRecoverError').click(function() { autoRecoverError = this.checked; });
|
||||
$('#enableStreaming').click(function() { enableStreaming = this.checked; loadStream($('#streamURL').val()); });
|
||||
$('#autoRecoverError').click(function() { autoRecoverError = this.checked; updatePermalink();});
|
||||
$('#enableWorker').click(function() { enableWorker = this.checked; updatePermalink();});
|
||||
$('#enableStreaming').prop( "checked", enableStreaming );
|
||||
$('#autoRecoverError').prop( "checked", autoRecoverError );
|
||||
$('#enableWorker').prop( "checked", enableWorker );
|
||||
});
|
||||
|
||||
|
||||
'use strict';
|
||||
var hls,events, stats, enableStreaming = true, autoRecoverError = true;
|
||||
var hls,events, stats,
|
||||
enableStreaming = JSON.parse(getURLParam('enableStreaming',true))
|
||||
autoRecoverError = JSON.parse(getURLParam('autoRecoverError',true)),
|
||||
enableWorker = JSON.parse(getURLParam('enableWorker',true));
|
||||
var video = $('#video')[0];
|
||||
video.volume = 0.05;
|
||||
|
||||
var manifest = decodeURIComponent(location.search.split('src=')[1]);
|
||||
if(manifest === 'undefined') {
|
||||
manifest = 'http://www.streambox.fr/playlists/x36xhzz/x36xhzz.m3u8';
|
||||
}
|
||||
loadStream(manifest);
|
||||
loadStream(decodeURIComponent(getURLParam('src','http://www.streambox.fr/playlists/x36xhzz/x36xhzz.m3u8')));
|
||||
|
||||
|
||||
function loadStream(url) {
|
||||
hideCanvas();
|
||||
|
@ -230,10 +235,7 @@ $(document).ready(function() {
|
|||
}
|
||||
|
||||
$('#streamURL').val(url);
|
||||
var hlsLink = document.URL.split('?')[0] + '?src=' + encodeURIComponent(url);
|
||||
var description = 'permalink: ' + "<a href=\"" + hlsLink + "\">" + hlsLink + "</a>";
|
||||
$("#StreamPermalink").html(description);
|
||||
|
||||
updatePermalink();
|
||||
if(!enableStreaming) {
|
||||
$("#HlsStatus").text("Streaming disabled");
|
||||
return;
|
||||
|
@ -242,7 +244,7 @@ $(document).ready(function() {
|
|||
$("#HlsStatus").text('loading ' + url);
|
||||
events = { url : url, t0 : performance.now(), load : [], buffer : [], video : [], level : [], bitrate : []};
|
||||
recoverDecodingErrorDate = recoverSwapAudioCodecDate = null;
|
||||
hls = new Hls({debug:true});
|
||||
hls = new Hls({debug:true, enableWorker : enableWorker});
|
||||
$("#HlsStatus").text('loading manifest and attaching video element...');
|
||||
hls.loadSource(url);
|
||||
hls.attachMedia(video);
|
||||
|
@ -470,6 +472,7 @@ $(document).ready(function() {
|
|||
hls.destroy();
|
||||
break;
|
||||
}
|
||||
console.log($("#HlsStatus").text());
|
||||
}
|
||||
if(!stats) stats = {};
|
||||
// track all errors independently
|
||||
|
@ -908,6 +911,25 @@ function timeRangesToString(r) {
|
|||
}
|
||||
}
|
||||
|
||||
function getURLParam(sParam, defaultValue) {
|
||||
var sPageURL = window.location.search.substring(1);
|
||||
var sURLVariables = sPageURL.split('&');
|
||||
for (var i = 0; i < sURLVariables.length; i++) {
|
||||
var sParameterName = sURLVariables[i].split('=');
|
||||
if (sParameterName[0] == sParam) {
|
||||
return sParameterName[1];
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function updatePermalink() {
|
||||
var url = $('#streamURL').val();
|
||||
var hlsLink = document.URL.split('?')[0] + '?src=' + encodeURIComponent(url) + '&enableStreaming=' + enableStreaming + '&autoRecoverError=' + autoRecoverError + '&enableWorker=' + enableWorker;
|
||||
var description = 'permalink: ' + "<a href=\"" + hlsLink + "\">" + hlsLink + "</a>";
|
||||
$("#StreamPermalink").html(description);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
|
441
dashboard-ui/bower_components/hls.js/dist/hls.js
vendored
441
dashboard-ui/bower_components/hls.js/dist/hls.js
vendored
|
@ -532,7 +532,8 @@ var LevelController = (function () {
|
|||
i,
|
||||
bitrateSet = {},
|
||||
videoCodecFound = false,
|
||||
audioCodecFound = false;
|
||||
audioCodecFound = false,
|
||||
hls = this.hls;
|
||||
|
||||
// regroup redundant level together
|
||||
data.levels.forEach(function (level) {
|
||||
|
@ -565,32 +566,36 @@ var LevelController = (function () {
|
|||
}
|
||||
|
||||
// only keep level with supported audio/video codecs
|
||||
levels0 = levels0.filter(function (level) {
|
||||
levels = levels.filter(function (level) {
|
||||
var checkSupported = function checkSupported(codec) {
|
||||
return MediaSource.isTypeSupported('video/mp4;codecs=' + codec);
|
||||
};
|
||||
var audioCodec = level.audioCodec,
|
||||
videoCodec = level.videoCodec;
|
||||
|
||||
return (audioCodec && checkSupported(audioCodec) || !audioCodec) && (videoCodec && checkSupported(videoCodec) || !videoCodec);
|
||||
return (!audioCodec || checkSupported(audioCodec)) && (!videoCodec || checkSupported(videoCodec));
|
||||
});
|
||||
|
||||
// start bitrate is the first bitrate of the manifest
|
||||
bitrateStart = levels[0].bitrate;
|
||||
// sort level on bitrate
|
||||
levels.sort(function (a, b) {
|
||||
return a.bitrate - b.bitrate;
|
||||
});
|
||||
this._levels = levels;
|
||||
// find index of first level in sorted levels
|
||||
for (i = 0; i < levels.length; i++) {
|
||||
if (levels[i].bitrate === bitrateStart) {
|
||||
this._firstLevel = i;
|
||||
_utilsLogger.logger.log('manifest loaded,' + levels.length + ' level(s) found, first bitrate:' + bitrateStart);
|
||||
break;
|
||||
if (levels.length) {
|
||||
// start bitrate is the first bitrate of the manifest
|
||||
bitrateStart = levels[0].bitrate;
|
||||
// sort level on bitrate
|
||||
levels.sort(function (a, b) {
|
||||
return a.bitrate - b.bitrate;
|
||||
});
|
||||
this._levels = levels;
|
||||
// find index of first level in sorted levels
|
||||
for (i = 0; i < levels.length; i++) {
|
||||
if (levels[i].bitrate === bitrateStart) {
|
||||
this._firstLevel = i;
|
||||
_utilsLogger.logger.log('manifest loaded,' + levels.length + ' level(s) found, first bitrate:' + bitrateStart);
|
||||
break;
|
||||
}
|
||||
}
|
||||
hls.trigger(_events2['default'].MANIFEST_PARSED, { levels: this._levels, firstLevel: this._firstLevel, stats: data.stats });
|
||||
} else {
|
||||
hls.trigger(_events2['default'].ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.MANIFEST_PARSING_ERROR, fatal: true, url: hls.url, reason: 'no compatible level found in manifest' });
|
||||
}
|
||||
this.hls.trigger(_events2['default'].MANIFEST_PARSED, { levels: this._levels, firstLevel: this._firstLevel, stats: data.stats });
|
||||
return;
|
||||
}
|
||||
}, {
|
||||
|
@ -648,7 +653,8 @@ var LevelController = (function () {
|
|||
}
|
||||
/* try to switch to a redundant stream if any available.
|
||||
* if no redundant stream available, emergency switch down (if in auto mode and current level not 0)
|
||||
* otherwise, we cannot recover this network error ....
|
||||
* otherwise, we cannot recover this network error ...
|
||||
* don't raise FRAG_LOAD_ERROR and FRAG_LOAD_TIMEOUT as fatal, as it is handled by mediaController
|
||||
*/
|
||||
if (levelId !== undefined) {
|
||||
level = this._levels[levelId];
|
||||
|
@ -664,18 +670,19 @@ var LevelController = (function () {
|
|||
hls.abrController.nextAutoLevel = 0;
|
||||
} else if (level && level.details && level.details.live) {
|
||||
_utilsLogger.logger.warn('level controller,' + details + ' on live stream, discard');
|
||||
} else {
|
||||
_utilsLogger.logger.error('cannot recover ' + details + ' error');
|
||||
this._level = undefined;
|
||||
// stopping live reloading timer if any
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
// FRAG_LOAD_ERROR and FRAG_LOAD_TIMEOUT are handled by mediaController
|
||||
} else if (details !== _errors.ErrorDetails.FRAG_LOAD_ERROR && details !== _errors.ErrorDetails.FRAG_LOAD_TIMEOUT) {
|
||||
_utilsLogger.logger.error('cannot recover ' + details + ' error');
|
||||
this._level = undefined;
|
||||
// stopping live reloading timer if any
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
// redispatch same error but with fatal set to true
|
||||
data.fatal = true;
|
||||
hls.trigger(event, data);
|
||||
}
|
||||
// redispatch same error but with fatal set to true
|
||||
data.fatal = true;
|
||||
hls.trigger(event, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1194,26 +1201,30 @@ var MSEMediaController = (function () {
|
|||
this.appendError = 0;
|
||||
} catch (err) {
|
||||
// in case any error occured while appending, put back segment in mp4segments table
|
||||
//logger.error(`error while trying to append buffer:${err.message},try appending later`);
|
||||
_utilsLogger.logger.error('error while trying to append buffer:' + err.message + ',try appending later');
|
||||
this.mp4segments.unshift(segment);
|
||||
if (this.appendError) {
|
||||
this.appendError++;
|
||||
} else {
|
||||
this.appendError = 1;
|
||||
}
|
||||
var event = { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_APPEND_ERROR, frag: this.fragCurrent };
|
||||
/* with UHD content, we could get loop of quota exceeded error until
|
||||
browser is able to evict some data from sourcebuffer. retrying help recovering this
|
||||
*/
|
||||
if (this.appendError > this.config.appendErrorMaxRetry) {
|
||||
_utilsLogger.logger.log('fail ' + this.config.appendErrorMaxRetry + ' times to append segment in sourceBuffer');
|
||||
event.fatal = true;
|
||||
hls.trigger(_events2['default'].ERROR, event);
|
||||
this.state = State.ERROR;
|
||||
return;
|
||||
} else {
|
||||
event.fatal = false;
|
||||
hls.trigger(_events2['default'].ERROR, event);
|
||||
// just discard QuotaExceededError for now, and wait for the natural browser buffer eviction
|
||||
//http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror
|
||||
if (err.code !== 22) {
|
||||
if (this.appendError) {
|
||||
this.appendError++;
|
||||
} else {
|
||||
this.appendError = 1;
|
||||
}
|
||||
var event = { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_APPEND_ERROR, frag: this.fragCurrent };
|
||||
/* with UHD content, we could get loop of quota exceeded error until
|
||||
browser is able to evict some data from sourcebuffer. retrying help recovering this
|
||||
*/
|
||||
if (this.appendError > this.config.appendErrorMaxRetry) {
|
||||
_utilsLogger.logger.log('fail ' + this.config.appendErrorMaxRetry + ' times to append segment in sourceBuffer');
|
||||
event.fatal = true;
|
||||
hls.trigger(_events2['default'].ERROR, event);
|
||||
this.state = State.ERROR;
|
||||
return;
|
||||
} else {
|
||||
event.fatal = false;
|
||||
hls.trigger(_events2['default'].ERROR, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.state = State.APPENDING;
|
||||
|
@ -1807,6 +1818,7 @@ var MSEMediaController = (function () {
|
|||
this.demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata);
|
||||
}
|
||||
}
|
||||
this.fragLoadError = 0;
|
||||
}
|
||||
}, {
|
||||
key: 'onInitSegment',
|
||||
|
@ -1905,6 +1917,24 @@ var MSEMediaController = (function () {
|
|||
// abort fragment loading on errors
|
||||
case _errors.ErrorDetails.FRAG_LOAD_ERROR:
|
||||
case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT:
|
||||
var loadError = this.fragLoadError;
|
||||
if (loadError) {
|
||||
loadError++;
|
||||
} else {
|
||||
loadError = 1;
|
||||
}
|
||||
if (loadError <= this.config.fragLoadingMaxRetry) {
|
||||
this.fragLoadError = loadError;
|
||||
// retry loading
|
||||
this.state = State.IDLE;
|
||||
} else {
|
||||
_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.state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR:
|
||||
case _errors.ErrorDetails.LEVEL_LOAD_ERROR:
|
||||
case _errors.ErrorDetails.LEVEL_LOAD_TIMEOUT:
|
||||
|
@ -2691,7 +2721,7 @@ var AACDemuxer = (function () {
|
|||
break;
|
||||
}
|
||||
}
|
||||
this.remuxer.remux(this._aacTrack, { samples: [] }, { samples: [] }, timeOffset);
|
||||
this.remuxer.remux(this._aacTrack, { samples: [] }, { samples: [{ pts: pts, dts: pts, unit: id3.payload }] }, timeOffset);
|
||||
}
|
||||
}, {
|
||||
key: '_ADTStoAudioConfig',
|
||||
|
@ -3513,7 +3543,7 @@ module.exports = exports['default'];
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', {
|
||||
value: true
|
||||
value: true
|
||||
});
|
||||
|
||||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
|
||||
|
@ -3525,130 +3555,136 @@ var _utilsLogger = require('../utils/logger');
|
|||
//import Hex from '../utils/hex';
|
||||
|
||||
var ID3 = (function () {
|
||||
function ID3(data) {
|
||||
_classCallCheck(this, ID3);
|
||||
function ID3(data) {
|
||||
_classCallCheck(this, ID3);
|
||||
|
||||
this._hasTimeStamp = false;
|
||||
var offset = 0,
|
||||
byte1,
|
||||
byte2,
|
||||
byte3,
|
||||
byte4,
|
||||
tagSize,
|
||||
endPos,
|
||||
header,
|
||||
len;
|
||||
do {
|
||||
header = this.readUTF(data, offset, 3);
|
||||
offset += 3;
|
||||
// first check for ID3 header
|
||||
if (header === 'ID3') {
|
||||
// skip 24 bits
|
||||
offset += 3;
|
||||
// retrieve tag(s) length
|
||||
byte1 = data[offset++] & 0x7f;
|
||||
byte2 = data[offset++] & 0x7f;
|
||||
byte3 = data[offset++] & 0x7f;
|
||||
byte4 = data[offset++] & 0x7f;
|
||||
tagSize = (byte1 << 21) + (byte2 << 14) + (byte3 << 7) + byte4;
|
||||
endPos = offset + tagSize;
|
||||
//logger.log(`ID3 tag found, size/end: ${tagSize}/${endPos}`);
|
||||
this._hasTimeStamp = false;
|
||||
var offset = 0,
|
||||
byte1,
|
||||
byte2,
|
||||
byte3,
|
||||
byte4,
|
||||
tagSize,
|
||||
endPos,
|
||||
header,
|
||||
len;
|
||||
do {
|
||||
header = this.readUTF(data, offset, 3);
|
||||
offset += 3;
|
||||
// first check for ID3 header
|
||||
if (header === 'ID3') {
|
||||
// skip 24 bits
|
||||
offset += 3;
|
||||
// retrieve tag(s) length
|
||||
byte1 = data[offset++] & 0x7f;
|
||||
byte2 = data[offset++] & 0x7f;
|
||||
byte3 = data[offset++] & 0x7f;
|
||||
byte4 = data[offset++] & 0x7f;
|
||||
tagSize = (byte1 << 21) + (byte2 << 14) + (byte3 << 7) + byte4;
|
||||
endPos = offset + tagSize;
|
||||
//logger.log(`ID3 tag found, size/end: ${tagSize}/${endPos}`);
|
||||
|
||||
// read ID3 tags
|
||||
this._parseID3Frames(data, offset, endPos);
|
||||
offset = endPos;
|
||||
} else if (header === '3DI') {
|
||||
// http://id3.org/id3v2.4.0-structure chapter 3.4. ID3v2 footer
|
||||
offset += 7;
|
||||
_utilsLogger.logger.log('3DI footer found, end: ' + offset);
|
||||
} else {
|
||||
offset -= 3;
|
||||
len = offset;
|
||||
if (len) {
|
||||
//logger.log(`ID3 len: ${len}`);
|
||||
if (!this.hasTimeStamp) {
|
||||
_utilsLogger.logger.warn('ID3 tag found, but no timestamp');
|
||||
}
|
||||
this._length = len;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} while (true);
|
||||
// read ID3 tags
|
||||
this._parseID3Frames(data, offset, endPos);
|
||||
offset = endPos;
|
||||
} else if (header === '3DI') {
|
||||
// http://id3.org/id3v2.4.0-structure chapter 3.4. ID3v2 footer
|
||||
offset += 7;
|
||||
_utilsLogger.logger.log('3DI footer found, end: ' + offset);
|
||||
} else {
|
||||
offset -= 3;
|
||||
len = offset;
|
||||
if (len) {
|
||||
//logger.log(`ID3 len: ${len}`);
|
||||
if (!this.hasTimeStamp) {
|
||||
_utilsLogger.logger.warn('ID3 tag found, but no timestamp');
|
||||
}
|
||||
this._length = len;
|
||||
this._payload = data.subarray(0, len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
_createClass(ID3, [{
|
||||
key: 'readUTF',
|
||||
value: function readUTF(data, start, len) {
|
||||
|
||||
var result = '',
|
||||
offset = start,
|
||||
end = start + len;
|
||||
do {
|
||||
result += String.fromCharCode(data[offset++]);
|
||||
} while (offset < end);
|
||||
return result;
|
||||
}
|
||||
}, {
|
||||
key: '_parseID3Frames',
|
||||
value: function _parseID3Frames(data, offset, endPos) {
|
||||
var tagId, tagLen, tagStart, tagFlags, timestamp;
|
||||
while (offset + 8 <= endPos) {
|
||||
tagId = this.readUTF(data, offset, 4);
|
||||
offset += 4;
|
||||
|
||||
_createClass(ID3, [{
|
||||
key: 'readUTF',
|
||||
value: function readUTF(data, start, len) {
|
||||
tagLen = data[offset++] << 24 + data[offset++] << 16 + data[offset++] << 8 + data[offset++];
|
||||
|
||||
var result = '',
|
||||
offset = start,
|
||||
end = start + len;
|
||||
do {
|
||||
result += String.fromCharCode(data[offset++]);
|
||||
} while (offset < end);
|
||||
return result;
|
||||
}
|
||||
}, {
|
||||
key: '_parseID3Frames',
|
||||
value: function _parseID3Frames(data, offset, endPos) {
|
||||
var tagId, tagLen, tagStart, tagFlags, timestamp;
|
||||
while (offset + 8 <= endPos) {
|
||||
tagId = this.readUTF(data, offset, 4);
|
||||
offset += 4;
|
||||
tagFlags = data[offset++] << 8 + data[offset++];
|
||||
|
||||
tagLen = data[offset++] << 24 + data[offset++] << 16 + data[offset++] << 8 + data[offset++];
|
||||
tagStart = offset;
|
||||
//logger.log("ID3 tag id:" + tagId);
|
||||
switch (tagId) {
|
||||
case 'PRIV':
|
||||
//logger.log('parse frame:' + Hex.hexDump(data.subarray(offset,endPos)));
|
||||
// owner should be "com.apple.streaming.transportStreamTimestamp"
|
||||
if (this.readUTF(data, offset, 44) === 'com.apple.streaming.transportStreamTimestamp') {
|
||||
offset += 44;
|
||||
// smelling even better ! we found the right descriptor
|
||||
// skip null character (string end) + 3 first bytes
|
||||
offset += 4;
|
||||
|
||||
tagFlags = data[offset++] << 8 + data[offset++];
|
||||
// timestamp is 33 bit expressed as a big-endian eight-octet number, with the upper 31 bits set to zero.
|
||||
var pts33Bit = data[offset++] & 0x1;
|
||||
this._hasTimeStamp = true;
|
||||
|
||||
tagStart = offset;
|
||||
//logger.log("ID3 tag id:" + tagId);
|
||||
switch (tagId) {
|
||||
case 'PRIV':
|
||||
//logger.log('parse frame:' + Hex.hexDump(data.subarray(offset,endPos)));
|
||||
// owner should be "com.apple.streaming.transportStreamTimestamp"
|
||||
if (this.readUTF(data, offset, 44) === 'com.apple.streaming.transportStreamTimestamp') {
|
||||
offset += 44;
|
||||
// smelling even better ! we found the right descriptor
|
||||
// skip null character (string end) + 3 first bytes
|
||||
offset += 4;
|
||||
timestamp = ((data[offset++] << 23) + (data[offset++] << 15) + (data[offset++] << 7) + data[offset++]) / 45;
|
||||
|
||||
// timestamp is 33 bit expressed as a big-endian eight-octet number, with the upper 31 bits set to zero.
|
||||
var pts33Bit = data[offset++] & 0x1;
|
||||
this._hasTimeStamp = true;
|
||||
|
||||
timestamp = ((data[offset++] << 23) + (data[offset++] << 15) + (data[offset++] << 7) + data[offset++]) / 45;
|
||||
|
||||
if (pts33Bit) {
|
||||
timestamp += 47721858.84; // 2^32 / 90
|
||||
}
|
||||
timestamp = Math.round(timestamp);
|
||||
_utilsLogger.logger.trace('ID3 timestamp found: ' + timestamp);
|
||||
this._timeStamp = timestamp;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (pts33Bit) {
|
||||
timestamp += 47721858.84; // 2^32 / 90
|
||||
}
|
||||
timestamp = Math.round(timestamp);
|
||||
_utilsLogger.logger.trace('ID3 timestamp found: ' + timestamp);
|
||||
this._timeStamp = timestamp;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, {
|
||||
key: 'hasTimeStamp',
|
||||
get: function get() {
|
||||
return this._hasTimeStamp;
|
||||
}
|
||||
}, {
|
||||
key: 'timeStamp',
|
||||
get: function get() {
|
||||
return this._timeStamp;
|
||||
}
|
||||
}, {
|
||||
key: 'length',
|
||||
get: function get() {
|
||||
return this._length;
|
||||
}
|
||||
}]);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'hasTimeStamp',
|
||||
get: function get() {
|
||||
return this._hasTimeStamp;
|
||||
}
|
||||
}, {
|
||||
key: 'timeStamp',
|
||||
get: function get() {
|
||||
return this._timeStamp;
|
||||
}
|
||||
}, {
|
||||
key: 'length',
|
||||
get: function get() {
|
||||
return this._length;
|
||||
}
|
||||
}, {
|
||||
key: 'payload',
|
||||
get: function get() {
|
||||
return this._payload;
|
||||
}
|
||||
}]);
|
||||
|
||||
return ID3;
|
||||
return ID3;
|
||||
})();
|
||||
|
||||
exports['default'] = ID3;
|
||||
|
@ -4746,7 +4782,7 @@ var Hls = (function () {
|
|||
enableWorker: true,
|
||||
enableSoftwareAES: true,
|
||||
fragLoadingTimeOut: 20000,
|
||||
fragLoadingMaxRetry: 1,
|
||||
fragLoadingMaxRetry: 6,
|
||||
fragLoadingRetryDelay: 1000,
|
||||
fragLoadingLoopThreshold: 3,
|
||||
manifestLoadingTimeOut: 10000,
|
||||
|
@ -4754,7 +4790,7 @@ var Hls = (function () {
|
|||
manifestLoadingRetryDelay: 1000,
|
||||
// fpsDroppedMonitoringPeriod: 5000,
|
||||
// fpsDroppedMonitoringThreshold: 0.2,
|
||||
appendErrorMaxRetry: 200,
|
||||
appendErrorMaxRetry: 3,
|
||||
loader: _utilsXhrLoader2['default'],
|
||||
fLoader: undefined,
|
||||
pLoader: undefined,
|
||||
|
@ -5038,7 +5074,7 @@ var FragmentLoader = (function () {
|
|||
this.frag.loaded = 0;
|
||||
var config = this.hls.config;
|
||||
frag.loader = this.loader = typeof config.fLoader !== 'undefined' ? new config.fLoader(config) : new config.loader(config);
|
||||
this.loader.load(frag.url, 'arraybuffer', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), config.fragLoadingTimeOut, config.fragLoadingMaxRetry, config.fragLoadingRetryDelay, this.loadprogress.bind(this), frag);
|
||||
this.loader.load(frag.url, 'arraybuffer', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), config.fragLoadingTimeOut, 1, config.fragLoadingRetryDelay, this.loadprogress.bind(this), frag);
|
||||
}
|
||||
}, {
|
||||
key: 'loadsuccess',
|
||||
|
@ -5301,7 +5337,7 @@ var PlaylistLoader = (function () {
|
|||
if (avcdata.length > 2) {
|
||||
result = avcdata.shift() + '.';
|
||||
result += parseInt(avcdata.shift()).toString(16);
|
||||
result += ('00' + parseInt(avcdata.shift()).toString(16)).substr(-4);
|
||||
result += ('000' + parseInt(avcdata.shift()).toString(16)).substr(-4);
|
||||
} else {
|
||||
result = codec;
|
||||
}
|
||||
|
@ -5651,6 +5687,7 @@ var MP4 = (function () {
|
|||
var payload = Array.prototype.slice.call(arguments, 1),
|
||||
size = 0,
|
||||
i = payload.length,
|
||||
len = i,
|
||||
result,
|
||||
view;
|
||||
// calculate the total size we need to allocate
|
||||
|
@ -5662,7 +5699,7 @@ var MP4 = (function () {
|
|||
view.setUint32(0, result.byteLength);
|
||||
result.set(type, 4);
|
||||
// copy the payload into the result
|
||||
for (i = 0, size = 8; i < payload.length; i++) {
|
||||
for (i = 0, size = 8; i < len; i++) {
|
||||
result.set(payload[i], size);
|
||||
size += payload[i].byteLength;
|
||||
}
|
||||
|
@ -5818,7 +5855,10 @@ var MP4 = (function () {
|
|||
0xfc | 3, // lengthSizeMinusOne, hard-coded to 4 bytes
|
||||
0xE0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets
|
||||
].concat(sps).concat([track.pps.length // numOfPictureParameterSets
|
||||
]).concat(pps))); // "PPS"
|
||||
]).concat(pps))),
|
||||
// "PPS"
|
||||
width = track.width,
|
||||
height = track.height;
|
||||
//console.log('avcc:' + Hex.hexDump(avcc));
|
||||
return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, // reserved
|
||||
|
@ -5826,8 +5866,8 @@ var MP4 = (function () {
|
|||
0x00, 0x00, // pre_defined
|
||||
0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
|
||||
track.width >> 8 & 0xFF, track.width & 0xff, // width
|
||||
track.height >> 8 & 0xFF, track.height & 0xff, // height
|
||||
width >> 8 & 0xFF, width & 0xff, // width
|
||||
height >> 8 & 0xFF, height & 0xff, // height
|
||||
0x00, 0x48, 0x00, 0x00, // horizresolution
|
||||
0x00, 0x48, 0x00, 0x00, // vertresolution
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
|
@ -5843,16 +5883,17 @@ var MP4 = (function () {
|
|||
}, {
|
||||
key: 'esds',
|
||||
value: function esds(track) {
|
||||
var configlen = track.config.length;
|
||||
return new Uint8Array([0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
|
||||
0x03, // descriptor_type
|
||||
0x17 + track.config.length, // length
|
||||
0x17 + configlen, // length
|
||||
0x00, 0x01, //es_id
|
||||
0x00, // stream_priority
|
||||
|
||||
0x04, // descriptor_type
|
||||
0x0f + track.config.length, // length
|
||||
0x0f + configlen, // length
|
||||
0x40, //codec : mpeg4_audio
|
||||
0x15, // stream_type
|
||||
0x00, 0x00, 0x00, // buffer_size
|
||||
|
@ -5860,11 +5901,12 @@ var MP4 = (function () {
|
|||
0x00, 0x00, 0x00, 0x00, // avgBitrate
|
||||
|
||||
0x05 // descriptor_type
|
||||
].concat([track.config.length]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor
|
||||
].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor
|
||||
}
|
||||
}, {
|
||||
key: 'mp4a',
|
||||
value: function mp4a(track) {
|
||||
var audiosamplerate = track.audiosamplerate;
|
||||
return MP4.box(MP4.types.mp4a, new Uint8Array([0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x01, // data_reference_index
|
||||
|
@ -5872,7 +5914,7 @@ var MP4 = (function () {
|
|||
0x00, track.channelCount, // channelcount
|
||||
0x00, 0x10, // sampleSize:16bits
|
||||
0x00, 0x00, 0x00, 0x00, // reserved2
|
||||
track.audiosamplerate >> 8 & 0xFF, track.audiosamplerate & 0xff, //
|
||||
audiosamplerate >> 8 & 0xFF, audiosamplerate & 0xff, //
|
||||
0x00, 0x00]), MP4.box(MP4.types.esds, MP4.esds(track)));
|
||||
}
|
||||
}, {
|
||||
|
@ -5887,30 +5929,35 @@ var MP4 = (function () {
|
|||
}, {
|
||||
key: 'tkhd',
|
||||
value: function tkhd(track) {
|
||||
var id = track.id,
|
||||
duration = track.duration,
|
||||
width = track.width,
|
||||
height = track.height;
|
||||
return MP4.box(MP4.types.tkhd, new Uint8Array([0x00, // version 0
|
||||
0x00, 0x00, 0x07, // flags
|
||||
0x00, 0x00, 0x00, 0x00, // creation_time
|
||||
0x00, 0x00, 0x00, 0x00, // modification_time
|
||||
track.id >> 24 & 0xFF, track.id >> 16 & 0xFF, track.id >> 8 & 0xFF, track.id & 0xFF, // track_ID
|
||||
id >> 24 & 0xFF, id >> 16 & 0xFF, id >> 8 & 0xFF, id & 0xFF, // track_ID
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
track.duration >> 24, track.duration >> 16 & 0xFF, track.duration >> 8 & 0xFF, track.duration & 0xFF, // duration
|
||||
duration >> 24, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF, // duration
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, // layer
|
||||
0x00, 0x00, // alternate_group
|
||||
0x00, 0x00, // non-audio track volume
|
||||
0x00, 0x00, // reserved
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
|
||||
track.width >> 8 & 0xFF, track.width & 0xFF, 0x00, 0x00, // width
|
||||
track.height >> 8 & 0xFF, track.height & 0xFF, 0x00, 0x00 // height
|
||||
width >> 8 & 0xFF, width & 0xFF, 0x00, 0x00, // width
|
||||
height >> 8 & 0xFF, height & 0xFF, 0x00, 0x00 // height
|
||||
]));
|
||||
}
|
||||
}, {
|
||||
key: 'traf',
|
||||
value: function traf(track, baseMediaDecodeTime) {
|
||||
var sampleDependencyTable = MP4.sdtp(track);
|
||||
var sampleDependencyTable = MP4.sdtp(track),
|
||||
id = track.id;
|
||||
return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
track.id >> 24, track.id >> 16 & 0XFF, track.id >> 8 & 0XFF, track.id & 0xFF])), // track_ID
|
||||
id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF])), // track_ID
|
||||
MP4.box(MP4.types.tfdt, new Uint8Array([0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
baseMediaDecodeTime >> 24, baseMediaDecodeTime >> 16 & 0XFF, baseMediaDecodeTime >> 8 & 0XFF, baseMediaDecodeTime & 0xFF])), // baseMediaDecodeTime
|
||||
|
@ -5937,9 +5984,10 @@ var MP4 = (function () {
|
|||
}, {
|
||||
key: 'trex',
|
||||
value: function trex(track) {
|
||||
var id = track.id;
|
||||
return MP4.box(MP4.types.trex, new Uint8Array([0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
track.id >> 24, track.id >> 16 & 0XFF, track.id >> 8 & 0XFF, track.id & 0xFF, // track_ID
|
||||
id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF, // track_ID
|
||||
0x00, 0x00, 0x00, 0x01, // default_sample_description_index
|
||||
0x00, 0x00, 0x00, 0x00, // default_sample_duration
|
||||
0x00, 0x00, 0x00, 0x00, // default_sample_size
|
||||
|
@ -5949,21 +5997,32 @@ var MP4 = (function () {
|
|||
}, {
|
||||
key: 'trun',
|
||||
value: function trun(track, offset) {
|
||||
var samples, sample, i, array;
|
||||
samples = track.samples || [];
|
||||
array = new Uint8Array(12 + 16 * samples.length);
|
||||
offset += 8 + array.byteLength;
|
||||
var samples = track.samples || [],
|
||||
len = samples.length,
|
||||
arraylen = 12 + 16 * len,
|
||||
array = new Uint8Array(arraylen),
|
||||
i,
|
||||
sample,
|
||||
duration,
|
||||
size,
|
||||
flags,
|
||||
cts;
|
||||
offset += 8 + arraylen;
|
||||
array.set([0x00, // version 0
|
||||
0x00, 0x0f, 0x01, // flags
|
||||
samples.length >>> 24 & 0xFF, samples.length >>> 16 & 0xFF, samples.length >>> 8 & 0xFF, samples.length & 0xFF, // sample_count
|
||||
len >>> 24 & 0xFF, len >>> 16 & 0xFF, len >>> 8 & 0xFF, len & 0xFF, // sample_count
|
||||
offset >>> 24 & 0xFF, offset >>> 16 & 0xFF, offset >>> 8 & 0xFF, offset & 0xFF // data_offset
|
||||
], 0);
|
||||
for (i = 0; i < samples.length; i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
sample = samples[i];
|
||||
array.set([sample.duration >>> 24 & 0xFF, sample.duration >>> 16 & 0xFF, sample.duration >>> 8 & 0xFF, sample.duration & 0xFF, // sample_duration
|
||||
sample.size >>> 24 & 0xFF, sample.size >>> 16 & 0xFF, sample.size >>> 8 & 0xFF, sample.size & 0xFF, // sample_size
|
||||
sample.flags.isLeading << 2 | sample.flags.dependsOn, sample.flags.isDependedOn << 6 | sample.flags.hasRedundancy << 4 | sample.flags.paddingValue << 1 | sample.flags.isNonSync, sample.flags.degradPrio & 0xF0 << 8, sample.flags.degradPrio & 0x0F, // sample_flags
|
||||
sample.cts >>> 24 & 0xFF, sample.cts >>> 16 & 0xFF, sample.cts >>> 8 & 0xFF, sample.cts & 0xFF // sample_composition_time_offset
|
||||
duration = sample.duration;
|
||||
size = sample.size;
|
||||
flags = sample.flags;
|
||||
cts = sample.cts;
|
||||
array.set([duration >>> 24 & 0xFF, duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, // sample_duration
|
||||
size >>> 24 & 0xFF, size >>> 16 & 0xFF, size >>> 8 & 0xFF, size & 0xFF, // sample_size
|
||||
flags.isLeading << 2 | sample.flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xF0 << 8, flags.degradPrio & 0x0F, // sample_flags
|
||||
cts >>> 24 & 0xFF, cts >>> 16 & 0xFF, cts >>> 8 & 0xFF, cts & 0xFF // sample_composition_time_offset
|
||||
], 12 + 16 * i);
|
||||
}
|
||||
return MP4.box(MP4.types.trun, array);
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "hls.js",
|
||||
"version": "0.3.11",
|
||||
"version": "0.3.15",
|
||||
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
||||
"homepage": "https://github.com/dailymotion/hls.js",
|
||||
"authors": "Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>",
|
||||
|
|
|
@ -32,7 +32,7 @@ class LevelController {
|
|||
}
|
||||
|
||||
onManifestLoaded(event, data) {
|
||||
var levels0 = [], levels = [], bitrateStart, i, bitrateSet = {}, videoCodecFound = false, audioCodecFound = false;
|
||||
var levels0 = [], levels = [], bitrateStart, i, bitrateSet = {}, videoCodecFound = false, audioCodecFound = false, hls = this.hls;
|
||||
|
||||
// regroup redundant level together
|
||||
data.levels.forEach(level => {
|
||||
|
@ -65,31 +65,34 @@ class LevelController {
|
|||
}
|
||||
|
||||
// only keep level with supported audio/video codecs
|
||||
levels0 = levels0.filter(function(level) {
|
||||
levels = levels.filter(function(level) {
|
||||
var checkSupported = function(codec) { return MediaSource.isTypeSupported(`video/mp4;codecs=${codec}`);};
|
||||
var audioCodec = level.audioCodec, videoCodec = level.videoCodec;
|
||||
|
||||
return ((audioCodec && checkSupported(audioCodec)) || !audioCodec) &&
|
||||
((videoCodec && checkSupported(videoCodec)) || !videoCodec);
|
||||
|
||||
return (!audioCodec || checkSupported(audioCodec)) &&
|
||||
(!videoCodec || checkSupported(videoCodec));
|
||||
});
|
||||
|
||||
// start bitrate is the first bitrate of the manifest
|
||||
bitrateStart = levels[0].bitrate;
|
||||
// sort level on bitrate
|
||||
levels.sort(function (a, b) {
|
||||
return a.bitrate - b.bitrate;
|
||||
});
|
||||
this._levels = levels;
|
||||
// find index of first level in sorted levels
|
||||
for (i = 0; i < levels.length; i++) {
|
||||
if (levels[i].bitrate === bitrateStart) {
|
||||
this._firstLevel = i;
|
||||
logger.log(`manifest loaded,${levels.length} level(s) found, first bitrate:${bitrateStart}`);
|
||||
break;
|
||||
if(levels.length) {
|
||||
// start bitrate is the first bitrate of the manifest
|
||||
bitrateStart = levels[0].bitrate;
|
||||
// sort level on bitrate
|
||||
levels.sort(function (a, b) {
|
||||
return a.bitrate - b.bitrate;
|
||||
});
|
||||
this._levels = levels;
|
||||
// find index of first level in sorted levels
|
||||
for (i = 0; i < levels.length; i++) {
|
||||
if (levels[i].bitrate === bitrateStart) {
|
||||
this._firstLevel = i;
|
||||
logger.log(`manifest loaded,${levels.length} level(s) found, first bitrate:${bitrateStart}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
hls.trigger(Event.MANIFEST_PARSED, {levels: this._levels, firstLevel: this._firstLevel, stats: data.stats});
|
||||
} else {
|
||||
hls.trigger(Event.ERROR, {type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.MANIFEST_PARSING_ERROR, fatal: true, url: hls.url, reason: 'no compatible level found in manifest'});
|
||||
}
|
||||
this.hls.trigger(Event.MANIFEST_PARSED, {levels: this._levels, firstLevel: this._firstLevel, stats: data.stats});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -187,7 +190,8 @@ class LevelController {
|
|||
}
|
||||
/* try to switch to a redundant stream if any available.
|
||||
* if no redundant stream available, emergency switch down (if in auto mode and current level not 0)
|
||||
* otherwise, we cannot recover this network error ....
|
||||
* otherwise, we cannot recover this network error ...
|
||||
* don't raise FRAG_LOAD_ERROR and FRAG_LOAD_TIMEOUT as fatal, as it is handled by mediaController
|
||||
*/
|
||||
if (levelId !== undefined) {
|
||||
level = this._levels[levelId];
|
||||
|
@ -203,7 +207,8 @@ class LevelController {
|
|||
hls.abrController.nextAutoLevel = 0;
|
||||
} else if(level && level.details && level.details.live) {
|
||||
logger.warn(`level controller,${details} on live stream, discard`);
|
||||
} else {
|
||||
// FRAG_LOAD_ERROR and FRAG_LOAD_TIMEOUT are handled by mediaController
|
||||
} else if (details !== ErrorDetails.FRAG_LOAD_ERROR && details !== ErrorDetails.FRAG_LOAD_TIMEOUT) {
|
||||
logger.error(`cannot recover ${details} error`);
|
||||
this._level = undefined;
|
||||
// stopping live reloading timer if any
|
||||
|
|
|
@ -391,26 +391,30 @@ class MSEMediaController {
|
|||
this.appendError = 0;
|
||||
} catch(err) {
|
||||
// in case any error occured while appending, put back segment in mp4segments table
|
||||
//logger.error(`error while trying to append buffer:${err.message},try appending later`);
|
||||
logger.error(`error while trying to append buffer:${err.message},try appending later`);
|
||||
this.mp4segments.unshift(segment);
|
||||
if (this.appendError) {
|
||||
this.appendError++;
|
||||
} else {
|
||||
this.appendError = 1;
|
||||
}
|
||||
var event = {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_APPEND_ERROR, frag: this.fragCurrent};
|
||||
/* with UHD content, we could get loop of quota exceeded error until
|
||||
browser is able to evict some data from sourcebuffer. retrying help recovering this
|
||||
*/
|
||||
if (this.appendError > this.config.appendErrorMaxRetry) {
|
||||
logger.log(`fail ${this.config.appendErrorMaxRetry} times to append segment in sourceBuffer`);
|
||||
event.fatal = true;
|
||||
hls.trigger(Event.ERROR, event);
|
||||
this.state = State.ERROR;
|
||||
return;
|
||||
} else {
|
||||
event.fatal = false;
|
||||
hls.trigger(Event.ERROR, event);
|
||||
// just discard QuotaExceededError for now, and wait for the natural browser buffer eviction
|
||||
//http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror
|
||||
if(err.code !== 22) {
|
||||
if (this.appendError) {
|
||||
this.appendError++;
|
||||
} else {
|
||||
this.appendError = 1;
|
||||
}
|
||||
var event = {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_APPEND_ERROR, frag: this.fragCurrent};
|
||||
/* with UHD content, we could get loop of quota exceeded error until
|
||||
browser is able to evict some data from sourcebuffer. retrying help recovering this
|
||||
*/
|
||||
if (this.appendError > this.config.appendErrorMaxRetry) {
|
||||
logger.log(`fail ${this.config.appendErrorMaxRetry} times to append segment in sourceBuffer`);
|
||||
event.fatal = true;
|
||||
hls.trigger(Event.ERROR, event);
|
||||
this.state = State.ERROR;
|
||||
return;
|
||||
} else {
|
||||
event.fatal = false;
|
||||
hls.trigger(Event.ERROR, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.state = State.APPENDING;
|
||||
|
@ -1000,6 +1004,7 @@ class MSEMediaController {
|
|||
this.demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata);
|
||||
}
|
||||
}
|
||||
this.fragLoadError = 0;
|
||||
}
|
||||
|
||||
onInitSegment(event, data) {
|
||||
|
@ -1095,6 +1100,24 @@ class MSEMediaController {
|
|||
// abort fragment loading on errors
|
||||
case ErrorDetails.FRAG_LOAD_ERROR:
|
||||
case ErrorDetails.FRAG_LOAD_TIMEOUT:
|
||||
var loadError = this.fragLoadError;
|
||||
if(loadError) {
|
||||
loadError++;
|
||||
} else {
|
||||
loadError=1;
|
||||
}
|
||||
if (loadError <= this.config.fragLoadingMaxRetry) {
|
||||
this.fragLoadError = loadError;
|
||||
// retry loading
|
||||
this.state = State.IDLE;
|
||||
} else {
|
||||
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.state = State.ERROR;
|
||||
}
|
||||
break;
|
||||
case ErrorDetails.FRAG_LOOP_LOADING_ERROR:
|
||||
case ErrorDetails.LEVEL_LOAD_ERROR:
|
||||
case ErrorDetails.LEVEL_LOAD_TIMEOUT:
|
||||
|
|
|
@ -79,7 +79,7 @@ import {ErrorTypes, ErrorDetails} from '../errors';
|
|||
break;
|
||||
}
|
||||
}
|
||||
this.remuxer.remux(this._aacTrack,{samples : []}, {samples : []}, timeOffset);
|
||||
this.remuxer.remux(this._aacTrack,{samples : []}, {samples : [ { pts: pts, dts : pts, unit : id3.payload} ]}, timeOffset);
|
||||
}
|
||||
|
||||
_ADTStoAudioConfig(data, offset, audioCodec) {
|
||||
|
|
|
@ -41,6 +41,7 @@ import {logger} from '../utils/logger';
|
|||
logger.warn('ID3 tag found, but no timestamp');
|
||||
}
|
||||
this._length = len;
|
||||
this._payload = data.subarray(0,len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -117,6 +118,10 @@ import {logger} from '../utils/logger';
|
|||
return this._length;
|
||||
}
|
||||
|
||||
get payload() {
|
||||
return this._payload;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ID3;
|
||||
|
|
|
@ -46,7 +46,7 @@ class Hls {
|
|||
enableWorker: true,
|
||||
enableSoftwareAES: true,
|
||||
fragLoadingTimeOut: 20000,
|
||||
fragLoadingMaxRetry: 1,
|
||||
fragLoadingMaxRetry: 6,
|
||||
fragLoadingRetryDelay: 1000,
|
||||
fragLoadingLoopThreshold: 3,
|
||||
manifestLoadingTimeOut: 10000,
|
||||
|
@ -54,7 +54,7 @@ class Hls {
|
|||
manifestLoadingRetryDelay: 1000,
|
||||
// fpsDroppedMonitoringPeriod: 5000,
|
||||
// fpsDroppedMonitoringThreshold: 0.2,
|
||||
appendErrorMaxRetry: 200,
|
||||
appendErrorMaxRetry: 3,
|
||||
loader: XhrLoader,
|
||||
fLoader: undefined,
|
||||
pLoader: undefined,
|
||||
|
|
|
@ -27,9 +27,9 @@ class FragmentLoader {
|
|||
this.frag.loaded = 0;
|
||||
var config = this.hls.config;
|
||||
frag.loader = this.loader = typeof(config.fLoader) !== 'undefined' ? new config.fLoader(config) : new config.loader(config);
|
||||
this.loader.load(frag.url, 'arraybuffer', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), config.fragLoadingTimeOut, config.fragLoadingMaxRetry, config.fragLoadingRetryDelay, this.loadprogress.bind(this), frag);
|
||||
this.loader.load(frag.url, 'arraybuffer', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), config.fragLoadingTimeOut, 1, config.fragLoadingRetryDelay, this.loadprogress.bind(this), frag);
|
||||
}
|
||||
|
||||
|
||||
loadsuccess(event, stats) {
|
||||
var payload = event.currentTarget.response;
|
||||
stats.length = payload.byteLength;
|
||||
|
@ -41,7 +41,7 @@ class FragmentLoader {
|
|||
loaderror(event) {
|
||||
this.loader.abort();
|
||||
this.hls.trigger(Event.ERROR, {type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.FRAG_LOAD_ERROR, fatal: false, frag: this.frag, response: event});
|
||||
}
|
||||
}
|
||||
|
||||
loadtimeout() {
|
||||
this.loader.abort();
|
||||
|
|
|
@ -94,7 +94,7 @@ class PlaylistLoader {
|
|||
if (avcdata.length > 2) {
|
||||
result = avcdata.shift() + '.';
|
||||
result += parseInt(avcdata.shift()).toString(16);
|
||||
result += ('00' + parseInt(avcdata.shift()).toString(16)).substr(-4);
|
||||
result += ('000' + parseInt(avcdata.shift()).toString(16)).substr(-4);
|
||||
} else {
|
||||
result = codec;
|
||||
}
|
||||
|
|
|
@ -152,6 +152,7 @@ class MP4 {
|
|||
payload = Array.prototype.slice.call(arguments, 1),
|
||||
size = 0,
|
||||
i = payload.length,
|
||||
len = i,
|
||||
result,
|
||||
view;
|
||||
// calculate the total size we need to allocate
|
||||
|
@ -163,7 +164,7 @@ class MP4 {
|
|||
view.setUint32(0, result.byteLength);
|
||||
result.set(type, 4);
|
||||
// copy the payload into the result
|
||||
for (i = 0, size = 8; i < payload.length; i++) {
|
||||
for (i = 0, size = 8; i < len; i++) {
|
||||
result.set(payload[i], size);
|
||||
size += payload[i].byteLength;
|
||||
}
|
||||
|
@ -341,7 +342,9 @@ class MP4 {
|
|||
0xE0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets
|
||||
].concat(sps).concat([
|
||||
track.pps.length // numOfPictureParameterSets
|
||||
]).concat(pps))); // "PPS"
|
||||
]).concat(pps))), // "PPS"
|
||||
width = track.width,
|
||||
height = track.height;
|
||||
//console.log('avcc:' + Hex.hexDump(avcc));
|
||||
return MP4.box(MP4.types.avc1, new Uint8Array([
|
||||
0x00, 0x00, 0x00, // reserved
|
||||
|
@ -352,10 +355,10 @@ class MP4 {
|
|||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, // pre_defined
|
||||
(track.width >> 8) & 0xFF,
|
||||
track.width & 0xff, // width
|
||||
(track.height >> 8) & 0xFF,
|
||||
track.height & 0xff, // height
|
||||
(width >> 8) & 0xFF,
|
||||
width & 0xff, // width
|
||||
(height >> 8) & 0xFF,
|
||||
height & 0xff, // height
|
||||
0x00, 0x48, 0x00, 0x00, // horizresolution
|
||||
0x00, 0x48, 0x00, 0x00, // vertresolution
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
|
@ -380,17 +383,18 @@ class MP4 {
|
|||
}
|
||||
|
||||
static esds(track) {
|
||||
var configlen = track.config.length;
|
||||
return new Uint8Array([
|
||||
0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
|
||||
0x03, // descriptor_type
|
||||
0x17+track.config.length, // length
|
||||
0x17+configlen, // length
|
||||
0x00, 0x01, //es_id
|
||||
0x00, // stream_priority
|
||||
|
||||
0x04, // descriptor_type
|
||||
0x0f+track.config.length, // length
|
||||
0x0f+configlen, // length
|
||||
0x40, //codec : mpeg4_audio
|
||||
0x15, // stream_type
|
||||
0x00, 0x00, 0x00, // buffer_size
|
||||
|
@ -398,23 +402,24 @@ class MP4 {
|
|||
0x00, 0x00, 0x00, 0x00, // avgBitrate
|
||||
|
||||
0x05 // descriptor_type
|
||||
].concat([track.config.length]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor
|
||||
].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor
|
||||
}
|
||||
|
||||
static mp4a(track) {
|
||||
return MP4.box(MP4.types.mp4a, new Uint8Array([
|
||||
0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x01, // data_reference_index
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, track.channelCount, // channelcount
|
||||
0x00, 0x10, // sampleSize:16bits
|
||||
0x00, 0x00, 0x00, 0x00, // reserved2
|
||||
(track.audiosamplerate >> 8) & 0xFF,
|
||||
track.audiosamplerate & 0xff, //
|
||||
0x00, 0x00]),
|
||||
MP4.box(MP4.types.esds, MP4.esds(track)));
|
||||
var audiosamplerate = track.audiosamplerate;
|
||||
return MP4.box(MP4.types.mp4a, new Uint8Array([
|
||||
0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x01, // data_reference_index
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, track.channelCount, // channelcount
|
||||
0x00, 0x10, // sampleSize:16bits
|
||||
0x00, 0x00, 0x00, 0x00, // reserved2
|
||||
(audiosamplerate >> 8) & 0xFF,
|
||||
audiosamplerate & 0xff, //
|
||||
0x00, 0x00]),
|
||||
MP4.box(MP4.types.esds, MP4.esds(track)));
|
||||
}
|
||||
|
||||
static stsd(track) {
|
||||
|
@ -426,20 +431,24 @@ class MP4 {
|
|||
}
|
||||
|
||||
static tkhd(track) {
|
||||
var id = track.id,
|
||||
duration = track.duration,
|
||||
width = track.width,
|
||||
height = track.height;
|
||||
return MP4.box(MP4.types.tkhd, new Uint8Array([
|
||||
0x00, // version 0
|
||||
0x00, 0x00, 0x07, // flags
|
||||
0x00, 0x00, 0x00, 0x00, // creation_time
|
||||
0x00, 0x00, 0x00, 0x00, // modification_time
|
||||
(track.id >> 24) & 0xFF,
|
||||
(track.id >> 16) & 0xFF,
|
||||
(track.id >> 8) & 0xFF,
|
||||
track.id & 0xFF, // track_ID
|
||||
(id >> 24) & 0xFF,
|
||||
(id >> 16) & 0xFF,
|
||||
(id >> 8) & 0xFF,
|
||||
id & 0xFF, // track_ID
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
(track.duration >> 24),
|
||||
(track.duration >> 16) & 0xFF,
|
||||
(track.duration >> 8) & 0xFF,
|
||||
track.duration & 0xFF, // duration
|
||||
(duration >> 24),
|
||||
(duration >> 16) & 0xFF,
|
||||
(duration >> 8) & 0xFF,
|
||||
duration & 0xFF, // duration
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, // layer
|
||||
|
@ -455,25 +464,26 @@ class MP4 {
|
|||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
|
||||
(track.width >> 8) & 0xFF,
|
||||
track.width & 0xFF,
|
||||
(width >> 8) & 0xFF,
|
||||
width & 0xFF,
|
||||
0x00, 0x00, // width
|
||||
(track.height >> 8) & 0xFF,
|
||||
track.height & 0xFF,
|
||||
(height >> 8) & 0xFF,
|
||||
height & 0xFF,
|
||||
0x00, 0x00 // height
|
||||
]));
|
||||
}
|
||||
|
||||
static traf(track,baseMediaDecodeTime) {
|
||||
var sampleDependencyTable = MP4.sdtp(track);
|
||||
var sampleDependencyTable = MP4.sdtp(track),
|
||||
id = track.id;
|
||||
return MP4.box(MP4.types.traf,
|
||||
MP4.box(MP4.types.tfhd, new Uint8Array([
|
||||
0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
(track.id >> 24),
|
||||
(track.id >> 16) & 0XFF,
|
||||
(track.id >> 8) & 0XFF,
|
||||
(track.id & 0xFF) // track_ID
|
||||
(id >> 24),
|
||||
(id >> 16) & 0XFF,
|
||||
(id >> 8) & 0XFF,
|
||||
(id & 0xFF) // track_ID
|
||||
])),
|
||||
MP4.box(MP4.types.tfdt, new Uint8Array([
|
||||
0x00, // version 0
|
||||
|
@ -505,13 +515,14 @@ class MP4 {
|
|||
}
|
||||
|
||||
static trex(track) {
|
||||
var id = track.id;
|
||||
return MP4.box(MP4.types.trex, new Uint8Array([
|
||||
0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
(track.id >> 24),
|
||||
(track.id >> 16) & 0XFF,
|
||||
(track.id >> 8) & 0XFF,
|
||||
(track.id & 0xFF), // track_ID
|
||||
(id >> 24),
|
||||
(id >> 16) & 0XFF,
|
||||
(id >> 8) & 0XFF,
|
||||
(id & 0xFF), // track_ID
|
||||
0x00, 0x00, 0x00, 0x01, // default_sample_description_index
|
||||
0x00, 0x00, 0x00, 0x00, // default_sample_duration
|
||||
0x00, 0x00, 0x00, 0x00, // default_sample_size
|
||||
|
@ -520,44 +531,50 @@ class MP4 {
|
|||
}
|
||||
|
||||
static trun(track, offset) {
|
||||
var samples, sample, i, array;
|
||||
samples = track.samples || [];
|
||||
array = new Uint8Array(12 + (16 * samples.length));
|
||||
offset += 8 + array.byteLength;
|
||||
var samples= track.samples || [],
|
||||
len = samples.length,
|
||||
arraylen = 12 + (16 * len),
|
||||
array = new Uint8Array(arraylen),
|
||||
i,sample,duration,size,flags,cts;
|
||||
offset += 8 + arraylen;
|
||||
array.set([
|
||||
0x00, // version 0
|
||||
0x00, 0x0f, 0x01, // flags
|
||||
(samples.length >>> 24) & 0xFF,
|
||||
(samples.length >>> 16) & 0xFF,
|
||||
(samples.length >>> 8) & 0xFF,
|
||||
samples.length & 0xFF, // sample_count
|
||||
(len >>> 24) & 0xFF,
|
||||
(len >>> 16) & 0xFF,
|
||||
(len >>> 8) & 0xFF,
|
||||
len & 0xFF, // sample_count
|
||||
(offset >>> 24) & 0xFF,
|
||||
(offset >>> 16) & 0xFF,
|
||||
(offset >>> 8) & 0xFF,
|
||||
offset & 0xFF // data_offset
|
||||
],0);
|
||||
for (i = 0; i < samples.length; i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
sample = samples[i];
|
||||
duration = sample.duration;
|
||||
size = sample.size;
|
||||
flags = sample.flags;
|
||||
cts = sample.cts;
|
||||
array.set([
|
||||
(sample.duration >>> 24) & 0xFF,
|
||||
(sample.duration >>> 16) & 0xFF,
|
||||
(sample.duration >>> 8) & 0xFF,
|
||||
sample.duration & 0xFF, // sample_duration
|
||||
(sample.size >>> 24) & 0xFF,
|
||||
(sample.size >>> 16) & 0xFF,
|
||||
(sample.size >>> 8) & 0xFF,
|
||||
sample.size & 0xFF, // sample_size
|
||||
(sample.flags.isLeading << 2) | sample.flags.dependsOn,
|
||||
(sample.flags.isDependedOn << 6) |
|
||||
(sample.flags.hasRedundancy << 4) |
|
||||
(sample.flags.paddingValue << 1) |
|
||||
sample.flags.isNonSync,
|
||||
sample.flags.degradPrio & 0xF0 << 8,
|
||||
sample.flags.degradPrio & 0x0F, // sample_flags
|
||||
(sample.cts >>> 24) & 0xFF,
|
||||
(sample.cts >>> 16) & 0xFF,
|
||||
(sample.cts >>> 8) & 0xFF,
|
||||
sample.cts & 0xFF // sample_composition_time_offset
|
||||
(duration >>> 24) & 0xFF,
|
||||
(duration >>> 16) & 0xFF,
|
||||
(duration >>> 8) & 0xFF,
|
||||
duration & 0xFF, // sample_duration
|
||||
(size >>> 24) & 0xFF,
|
||||
(size >>> 16) & 0xFF,
|
||||
(size >>> 8) & 0xFF,
|
||||
size & 0xFF, // sample_size
|
||||
(flags.isLeading << 2) | sample.flags.dependsOn,
|
||||
(flags.isDependedOn << 6) |
|
||||
(flags.hasRedundancy << 4) |
|
||||
(flags.paddingValue << 1) |
|
||||
flags.isNonSync,
|
||||
flags.degradPrio & 0xF0 << 8,
|
||||
flags.degradPrio & 0x0F, // sample_flags
|
||||
(cts >>> 24) & 0xFF,
|
||||
(cts >>> 16) & 0xFF,
|
||||
(cts >>> 8) & 0xFF,
|
||||
cts & 0xFF // sample_composition_time_offset
|
||||
],12+16*i);
|
||||
}
|
||||
return MP4.box(MP4.types.trun, array);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue