mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
update components
This commit is contained in:
parent
53c2a5fe3c
commit
24ad560e5a
16 changed files with 223 additions and 126 deletions
|
@ -15,12 +15,12 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
"devDependencies": {},
|
||||||
"ignore": [],
|
"ignore": [],
|
||||||
"version": "1.0.24",
|
"version": "1.0.26",
|
||||||
"_release": "1.0.24",
|
"_release": "1.0.26",
|
||||||
"_resolution": {
|
"_resolution": {
|
||||||
"type": "version",
|
"type": "version",
|
||||||
"tag": "1.0.24",
|
"tag": "1.0.26",
|
||||||
"commit": "ab1c97700a5059e3522a8ec5c43fa33ee36d6e20"
|
"commit": "f3cd4149e26d75861ab29a3e44647195bb97728b"
|
||||||
},
|
},
|
||||||
"_source": "git://github.com/MediaBrowser/emby-webcomponents.git",
|
"_source": "git://github.com/MediaBrowser/emby-webcomponents.git",
|
||||||
"_target": "~1.0.0",
|
"_target": "~1.0.0",
|
||||||
|
|
|
@ -84,18 +84,21 @@ define(['browser'], function (browser) {
|
||||||
|
|
||||||
var videoAudioCodecs = [];
|
var videoAudioCodecs = [];
|
||||||
|
|
||||||
|
var supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, '') ||
|
||||||
|
videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, '');
|
||||||
|
|
||||||
// Only put mp3 first if mkv support is there
|
// Only put mp3 first if mkv support is there
|
||||||
// Otherwise with HLS and mp3 audio we're seeing some browsers
|
// Otherwise with HLS and mp3 audio we're seeing some browsers
|
||||||
if (canPlayMkv) {
|
if (canPlayMkv) {
|
||||||
if (canPlayAudioFormat('mp3')) {
|
if (supportsMp3VideoAudio) {
|
||||||
videoAudioCodecs.push('mp3');
|
videoAudioCodecs.push('mp3');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (canPlayAudioFormat('aac')) {
|
if (videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"').replace(/no/, '')) {
|
||||||
videoAudioCodecs.push('aac');
|
videoAudioCodecs.push('aac');
|
||||||
}
|
}
|
||||||
if (!canPlayMkv) {
|
if (!canPlayMkv) {
|
||||||
if (canPlayAudioFormat('mp3')) {
|
if (supportsMp3VideoAudio) {
|
||||||
videoAudioCodecs.push('mp3');
|
videoAudioCodecs.push('mp3');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,10 +225,11 @@ define(['browser'], function (browser) {
|
||||||
|
|
||||||
var videoAudioChannels = '6';
|
var videoAudioChannels = '6';
|
||||||
|
|
||||||
|
// Handle he-aac not supported
|
||||||
|
if (!videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.5"').replace(/no/, '')) {
|
||||||
profile.CodecProfiles.push({
|
profile.CodecProfiles.push({
|
||||||
Type: 'VideoAudio',
|
Type: 'VideoAudio',
|
||||||
Codec: 'aac',
|
Codec: 'aac',
|
||||||
Container: 'mkv,mov',
|
|
||||||
Conditions: [
|
Conditions: [
|
||||||
{
|
{
|
||||||
Condition: 'NotEquals',
|
Condition: 'NotEquals',
|
||||||
|
@ -243,15 +247,9 @@ define(['browser'], function (browser) {
|
||||||
Value: 'false',
|
Value: 'false',
|
||||||
IsRequired: 'false'
|
IsRequired: 'false'
|
||||||
}
|
}
|
||||||
// Disabling this is going to require us to learn why it was disabled in the first place
|
|
||||||
//,
|
|
||||||
//{
|
|
||||||
// Condition: 'NotEquals',
|
|
||||||
// Property: 'AudioProfile',
|
|
||||||
// Value: 'LC'
|
|
||||||
//}
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
profile.CodecProfiles.push({
|
profile.CodecProfiles.push({
|
||||||
Type: 'VideoAudio',
|
Type: 'VideoAudio',
|
||||||
|
|
|
@ -35,8 +35,6 @@ define(function () {
|
||||||
|
|
||||||
var url = cssId + '.css';
|
var url = cssId + '.css';
|
||||||
|
|
||||||
var packageName = '';
|
|
||||||
|
|
||||||
if (url.indexOf('http') != 0 && url.indexOf('file:') != 0) {
|
if (url.indexOf('http') != 0 && url.indexOf('file:') != 0) {
|
||||||
url = config.baseUrl + url;
|
url = config.baseUrl + url;
|
||||||
}
|
}
|
||||||
|
@ -46,10 +44,6 @@ define(function () {
|
||||||
|
|
||||||
var link = document.createElement('link');
|
var link = document.createElement('link');
|
||||||
|
|
||||||
if (packageName) {
|
|
||||||
link.setAttribute('data-package', packageName);
|
|
||||||
}
|
|
||||||
|
|
||||||
link.setAttribute('rel', 'stylesheet');
|
link.setAttribute('rel', 'stylesheet');
|
||||||
link.setAttribute('type', 'text/css');
|
link.setAttribute('type', 'text/css');
|
||||||
link.onload = load;
|
link.onload = load;
|
||||||
|
@ -61,19 +55,11 @@ define(function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.requireCss = {
|
window.requireCss = {
|
||||||
unloadPackage: function (packageName) {
|
removeStylesheet: function (stylesheet) {
|
||||||
|
|
||||||
// Todo: unload css here
|
|
||||||
var stylesheets = document.head.querySelectorAll("link[data-package='" + packageName + "']");
|
|
||||||
for (var i = 0, length = stylesheets.length; i < length; i++) {
|
|
||||||
|
|
||||||
var stylesheet = stylesheets[i];
|
|
||||||
|
|
||||||
console.log('Unloading stylesheet: ' + stylesheet.href);
|
|
||||||
stylesheet.parentNode.removeChild(stylesheet);
|
stylesheet.parentNode.removeChild(stylesheet);
|
||||||
removeFromLoadHistory(stylesheet.href);
|
removeFromLoadHistory(stylesheet.href);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return requireCss;
|
return requireCss;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "hls.js",
|
"name": "hls.js",
|
||||||
"version": "0.4.6",
|
"version": "0.4.7",
|
||||||
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
||||||
"homepage": "https://github.com/dailymotion/hls.js",
|
"homepage": "https://github.com/dailymotion/hls.js",
|
||||||
"authors": [
|
"authors": [
|
||||||
|
@ -15,11 +15,11 @@
|
||||||
"test",
|
"test",
|
||||||
"tests"
|
"tests"
|
||||||
],
|
],
|
||||||
"_release": "0.4.6",
|
"_release": "0.4.7",
|
||||||
"_resolution": {
|
"_resolution": {
|
||||||
"type": "version",
|
"type": "version",
|
||||||
"tag": "v0.4.6",
|
"tag": "v0.4.7",
|
||||||
"commit": "eb4bfb0ebadda9797d8020e7b3a2d2c699444cab"
|
"commit": "9c1a7e7d768346dcc4012e8b80f73e823af5ff20"
|
||||||
},
|
},
|
||||||
"_source": "git://github.com/dailymotion/hls.js.git",
|
"_source": "git://github.com/dailymotion/hls.js.git",
|
||||||
"_target": "~0.4.5",
|
"_target": "~0.4.5",
|
||||||
|
|
1
dashboard-ui/bower_components/hls.js/API.md
vendored
1
dashboard-ui/bower_components/hls.js/API.md
vendored
|
@ -99,6 +99,7 @@ each error is categorized by :
|
||||||
- ```Hls.ErrorDetails.FRAG_PARSING_ERROR```raised when fragment parsing fails
|
- ```Hls.ErrorDetails.FRAG_PARSING_ERROR```raised when fragment parsing fails
|
||||||
- ```Hls.ErrorDetails.BUFFER_APPEND_ERROR```raised when exception is raised while preparing buffer append
|
- ```Hls.ErrorDetails.BUFFER_APPEND_ERROR```raised when exception is raised while preparing buffer append
|
||||||
- ```Hls.ErrorDetails.BUFFER_APPENDING_ERROR```raised when exception is raised during buffer appending
|
- ```Hls.ErrorDetails.BUFFER_APPENDING_ERROR```raised when exception is raised during buffer appending
|
||||||
|
- ```Hls.ErrorDetails.BUFFER_STALLED_ERROR```raised when playback stalls because the buffer runs out
|
||||||
- its fatality:
|
- its fatality:
|
||||||
- ```false```if error is not fatal, hls.js will try to recover it
|
- ```false```if error is not fatal, hls.js will try to recover it
|
||||||
- ```true```if error is fatal, an action is required to (try to) recover it.
|
- ```true```if error is fatal, an action is required to (try to) recover it.
|
||||||
|
|
94
dashboard-ui/bower_components/hls.js/dist/hls.js
vendored
94
dashboard-ui/bower_components/hls.js/dist/hls.js
vendored
|
@ -838,7 +838,8 @@ var State = {
|
||||||
PARSING: 5,
|
PARSING: 5,
|
||||||
PARSED: 6,
|
PARSED: 6,
|
||||||
APPENDING: 7,
|
APPENDING: 7,
|
||||||
BUFFER_FLUSHING: 8
|
BUFFER_FLUSHING: 8,
|
||||||
|
ENDED: 9
|
||||||
};
|
};
|
||||||
|
|
||||||
var MSEMediaController = (function (_EventHandler) {
|
var MSEMediaController = (function (_EventHandler) {
|
||||||
|
@ -902,6 +903,7 @@ var MSEMediaController = (function (_EventHandler) {
|
||||||
this.mp4segments = [];
|
this.mp4segments = [];
|
||||||
this.flushRange = [];
|
this.flushRange = [];
|
||||||
this.bufferRange = [];
|
this.bufferRange = [];
|
||||||
|
this.stalled = false;
|
||||||
var frag = this.fragCurrent;
|
var frag = this.fragCurrent;
|
||||||
if (frag) {
|
if (frag) {
|
||||||
if (frag.loader) {
|
if (frag.loader) {
|
||||||
|
@ -1083,13 +1085,23 @@ var MSEMediaController = (function (_EventHandler) {
|
||||||
// have we reached end of VOD playlist ?
|
// have we reached end of VOD playlist ?
|
||||||
if (!levelDetails.live) {
|
if (!levelDetails.live) {
|
||||||
var mediaSource = this.mediaSource;
|
var mediaSource = this.mediaSource;
|
||||||
if (mediaSource && mediaSource.readyState === 'open') {
|
if (mediaSource) {
|
||||||
// ensure sourceBuffer are not in updating states
|
switch (mediaSource.readyState) {
|
||||||
|
case 'open':
|
||||||
var sb = this.sourceBuffer;
|
var sb = this.sourceBuffer;
|
||||||
if (!(sb.audio && sb.audio.updating || sb.video && sb.video.updating)) {
|
if (!(sb.audio && sb.audio.updating || sb.video && sb.video.updating)) {
|
||||||
_utilsLogger.logger.log('all media data available, signal endOfStream() to MediaSource');
|
_utilsLogger.logger.log('all media data available, signal endOfStream() to MediaSource and stop loading fragment');
|
||||||
//Notify the media element that it now has all of the media data
|
//Notify the media element that it now has all of the media data
|
||||||
mediaSource.endOfStream();
|
mediaSource.endOfStream();
|
||||||
|
this.state = State.ENDED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ended':
|
||||||
|
_utilsLogger.logger.log('all media data available and mediaSource ended, stop loading fragment');
|
||||||
|
this.state = State.ENDED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1275,6 +1287,8 @@ var MSEMediaController = (function (_EventHandler) {
|
||||||
each time sourceBuffer updateend() callback will be triggered
|
each time sourceBuffer updateend() callback will be triggered
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
|
case State.ENDED:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1682,6 +1696,9 @@ var MSEMediaController = (function (_EventHandler) {
|
||||||
// switch to IDLE state to load new fragment
|
// switch to IDLE state to load new fragment
|
||||||
this.state = State.IDLE;
|
this.state = State.IDLE;
|
||||||
}
|
}
|
||||||
|
} else if (this.state === State.ENDED) {
|
||||||
|
// switch to IDLE state to check for potential new fragment
|
||||||
|
this.state = State.IDLE;
|
||||||
}
|
}
|
||||||
if (this.media) {
|
if (this.media) {
|
||||||
this.lastCurrentTime = this.media.currentTime;
|
this.lastCurrentTime = this.media.currentTime;
|
||||||
|
@ -2017,17 +2034,27 @@ var MSEMediaController = (function (_EventHandler) {
|
||||||
var currentTime = media.currentTime,
|
var currentTime = media.currentTime,
|
||||||
bufferInfo = this.bufferInfo(currentTime, 0),
|
bufferInfo = this.bufferInfo(currentTime, 0),
|
||||||
isPlaying = !(media.paused || media.ended || media.seeking || readyState < 3),
|
isPlaying = !(media.paused || media.ended || media.seeking || readyState < 3),
|
||||||
jumpThreshold = 0.2;
|
jumpThreshold = 0.2,
|
||||||
|
playheadMoving = currentTime > media.playbackRate * this.lastCurrentTime;
|
||||||
|
|
||||||
|
if (this.stalled && playheadMoving) {
|
||||||
|
this.stalled = false;
|
||||||
|
}
|
||||||
|
|
||||||
// check buffer upfront
|
// check buffer upfront
|
||||||
// if less than 200ms is buffered, and media is playing but playhead is not moving,
|
// if less than 200ms is buffered, and media is playing but playhead is not moving,
|
||||||
// and we have a new buffer range available upfront, let's seek to that one
|
// and we have a new buffer range available upfront, let's seek to that one
|
||||||
if (bufferInfo.len <= jumpThreshold) {
|
if (bufferInfo.len <= jumpThreshold) {
|
||||||
if (currentTime > media.playbackRate * this.lastCurrentTime || !isPlaying) {
|
if (playheadMoving || !isPlaying) {
|
||||||
// playhead moving or media not playing
|
// playhead moving or media not playing
|
||||||
jumpThreshold = 0;
|
jumpThreshold = 0;
|
||||||
} else {
|
} else {
|
||||||
|
// playhead not moving AND media playing
|
||||||
_utilsLogger.logger.log('playback seems stuck');
|
_utilsLogger.logger.log('playback seems stuck');
|
||||||
|
if (!this.stalled) {
|
||||||
|
this.hls.trigger(_events2['default'].ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_STALLED_ERROR, fatal: false });
|
||||||
|
this.stalled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if we are below threshold, try to jump if next buffer range is close
|
// if we are below threshold, try to jump if next buffer range is close
|
||||||
if (bufferInfo.len <= jumpThreshold) {
|
if (bufferInfo.len <= jumpThreshold) {
|
||||||
|
@ -3821,6 +3848,8 @@ var TSDemuxer = (function () {
|
||||||
value: function switchLevel() {
|
value: function switchLevel() {
|
||||||
this.pmtParsed = false;
|
this.pmtParsed = false;
|
||||||
this._pmtId = -1;
|
this._pmtId = -1;
|
||||||
|
this.lastAacPTS = null;
|
||||||
|
this.aacOverFlow = null;
|
||||||
this._avcTrack = { type: 'video', id: -1, sequenceNumber: 0, samples: [], len: 0, nbNalu: 0 };
|
this._avcTrack = { type: 'video', id: -1, sequenceNumber: 0, samples: [], len: 0, nbNalu: 0 };
|
||||||
this._aacTrack = { type: 'audio', id: -1, sequenceNumber: 0, samples: [], len: 0 };
|
this._aacTrack = { type: 'audio', id: -1, sequenceNumber: 0, samples: [], len: 0 };
|
||||||
this._id3Track = { type: 'id3', id: -1, sequenceNumber: 0, samples: [], len: 0 };
|
this._id3Track = { type: 'id3', id: -1, sequenceNumber: 0, samples: [], len: 0 };
|
||||||
|
@ -4231,7 +4260,7 @@ var TSDemuxer = (function () {
|
||||||
case 3:
|
case 3:
|
||||||
if (value === 0) {
|
if (value === 0) {
|
||||||
state = 3;
|
state = 3;
|
||||||
} else if (value === 1) {
|
} else if (value === 1 && i < len) {
|
||||||
unitType = array[i] & 0x1f;
|
unitType = array[i] & 0x1f;
|
||||||
//logger.log('find NALU @ offset:' + i + ',type:' + unitType);
|
//logger.log('find NALU @ offset:' + i + ',type:' + unitType);
|
||||||
if (lastUnitStart) {
|
if (lastUnitStart) {
|
||||||
|
@ -4289,6 +4318,8 @@ var TSDemuxer = (function () {
|
||||||
startOffset = 0,
|
startOffset = 0,
|
||||||
duration = this._duration,
|
duration = this._duration,
|
||||||
audioCodec = this.audioCodec,
|
audioCodec = this.audioCodec,
|
||||||
|
aacOverFlow = this.aacOverFlow,
|
||||||
|
lastAacPTS = this.lastAacPTS,
|
||||||
config,
|
config,
|
||||||
frameLength,
|
frameLength,
|
||||||
frameDuration,
|
frameDuration,
|
||||||
|
@ -4298,10 +4329,11 @@ var TSDemuxer = (function () {
|
||||||
stamp,
|
stamp,
|
||||||
len,
|
len,
|
||||||
aacSample;
|
aacSample;
|
||||||
if (this.aacOverFlow) {
|
if (aacOverFlow) {
|
||||||
var tmp = new Uint8Array(this.aacOverFlow.byteLength + data.byteLength);
|
var tmp = new Uint8Array(aacOverFlow.byteLength + data.byteLength);
|
||||||
tmp.set(this.aacOverFlow, 0);
|
tmp.set(aacOverFlow, 0);
|
||||||
tmp.set(data, this.aacOverFlow.byteLength);
|
tmp.set(data, aacOverFlow.byteLength);
|
||||||
|
//logger.log(`AAC: append overflowing ${aacOverFlow.byteLength} bytes to beginning of new PES`);
|
||||||
data = tmp;
|
data = tmp;
|
||||||
}
|
}
|
||||||
// look for ADTS header (0xFFFx)
|
// look for ADTS header (0xFFFx)
|
||||||
|
@ -4337,17 +4369,28 @@ var TSDemuxer = (function () {
|
||||||
}
|
}
|
||||||
frameIndex = 0;
|
frameIndex = 0;
|
||||||
frameDuration = 1024 * 90000 / track.audiosamplerate;
|
frameDuration = 1024 * 90000 / track.audiosamplerate;
|
||||||
|
|
||||||
|
// if last AAC frame is overflowing, we should ensure timestamps are contiguous:
|
||||||
|
// first sample PTS should be equal to last sample PTS + frameDuration
|
||||||
|
if (aacOverFlow && lastAacPTS) {
|
||||||
|
var newPTS = lastAacPTS + frameDuration;
|
||||||
|
if (Math.abs(newPTS - pts) > 1) {
|
||||||
|
_utilsLogger.logger.log('AAC: align PTS for overlapping frames by ' + Math.round((newPTS - pts) / 90));
|
||||||
|
pts = newPTS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (offset + 5 < len) {
|
while (offset + 5 < len) {
|
||||||
// The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header
|
// The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header
|
||||||
headerLength = !!(data[offset + 1] & 0x01) ? 7 : 9;
|
headerLength = !!(data[offset + 1] & 0x01) ? 7 : 9;
|
||||||
// retrieve frame size
|
// retrieve frame size
|
||||||
frameLength = (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xE0) >>> 5;
|
frameLength = (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xE0) >>> 5;
|
||||||
frameLength -= headerLength;
|
frameLength -= headerLength;
|
||||||
stamp = Math.round(pts + frameIndex * frameDuration);
|
|
||||||
//stamp = pes.pts;
|
//stamp = pes.pts;
|
||||||
|
|
||||||
//console.log('AAC frame, offset/length/pts:' + (offset+headerLength) + '/' + frameLength + '/' + stamp.toFixed(0));
|
|
||||||
if (frameLength > 0 && offset + headerLength + frameLength <= len) {
|
if (frameLength > 0 && offset + headerLength + frameLength <= len) {
|
||||||
|
stamp = Math.round(pts + frameIndex * frameDuration);
|
||||||
|
//logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`);
|
||||||
aacSample = { unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp };
|
aacSample = { unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp };
|
||||||
track.samples.push(aacSample);
|
track.samples.push(aacSample);
|
||||||
track.len += frameLength;
|
track.len += frameLength;
|
||||||
|
@ -4364,10 +4407,13 @@ var TSDemuxer = (function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (offset < len) {
|
if (offset < len) {
|
||||||
this.aacOverFlow = data.subarray(offset, len);
|
aacOverFlow = data.subarray(offset, len);
|
||||||
|
//logger.log(`AAC: overflow detected:${len-offset}`);
|
||||||
} else {
|
} else {
|
||||||
this.aacOverFlow = null;
|
aacOverFlow = null;
|
||||||
}
|
}
|
||||||
|
this.aacOverFlow = aacOverFlow;
|
||||||
|
this.lastAacPTS = stamp;
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
key: '_parseID3PES',
|
key: '_parseID3PES',
|
||||||
|
@ -4438,7 +4484,9 @@ var ErrorDetails = {
|
||||||
// Identifier for a buffer append error - data: append error description
|
// Identifier for a buffer append error - data: append error description
|
||||||
BUFFER_APPEND_ERROR: 'bufferAppendError',
|
BUFFER_APPEND_ERROR: 'bufferAppendError',
|
||||||
// Identifier for a buffer appending error event - data: appending error description
|
// Identifier for a buffer appending error event - data: appending error description
|
||||||
BUFFER_APPENDING_ERROR: 'bufferAppendingError'
|
BUFFER_APPENDING_ERROR: 'bufferAppendingError',
|
||||||
|
// Identifier for a buffer stalled error event
|
||||||
|
BUFFER_STALLED_ERROR: 'bufferStalledError'
|
||||||
};
|
};
|
||||||
exports.ErrorDetails = ErrorDetails;
|
exports.ErrorDetails = ErrorDetails;
|
||||||
|
|
||||||
|
@ -6475,7 +6523,8 @@ var MP4Remuxer = (function () {
|
||||||
if (delta) {
|
if (delta) {
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
_utilsLogger.logger.log(delta + ' ms hole between AAC samples detected,filling it');
|
_utilsLogger.logger.log(delta + ' ms hole between AAC samples detected,filling it');
|
||||||
} else if (delta < 0) {
|
// if we have frame overlap, overlapping for more than half a frame duraion
|
||||||
|
} else if (delta < -12) {
|
||||||
// drop overlapping audio frames... browser will deal with it
|
// drop overlapping audio frames... browser will deal with it
|
||||||
_utilsLogger.logger.log(-delta + ' ms overlapping between AAC samples detected, drop frame');
|
_utilsLogger.logger.log(-delta + ' ms overlapping between AAC samples detected, drop frame');
|
||||||
track.len -= unit.byteLength;
|
track.len -= unit.byteLength;
|
||||||
|
@ -6683,7 +6732,7 @@ var AttrList = (function () {
|
||||||
}], [{
|
}], [{
|
||||||
key: 'parseAttrList',
|
key: 'parseAttrList',
|
||||||
value: function parseAttrList(input) {
|
value: function parseAttrList(input) {
|
||||||
var re = /(.+?)=((?:\".*?\")|.*?)(?:,|$)/g;
|
var re = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g;
|
||||||
var match,
|
var match,
|
||||||
attrs = {};
|
attrs = {};
|
||||||
while ((match = re.exec(input)) !== null) {
|
while ((match = re.exec(input)) !== null) {
|
||||||
|
@ -6983,7 +7032,14 @@ var XhrLoader = (function () {
|
||||||
}, {
|
}, {
|
||||||
key: 'loadInternal',
|
key: 'loadInternal',
|
||||||
value: function loadInternal() {
|
value: function loadInternal() {
|
||||||
var xhr = this.loader = new XMLHttpRequest();
|
var xhr;
|
||||||
|
|
||||||
|
if (typeof XDomainRequest !== 'undefined') {
|
||||||
|
xhr = this.loader = new XDomainRequest();
|
||||||
|
} else {
|
||||||
|
xhr = this.loader = new XMLHttpRequest();
|
||||||
|
}
|
||||||
|
|
||||||
xhr.onloadend = this.loadend.bind(this);
|
xhr.onloadend = this.loadend.bind(this);
|
||||||
xhr.onprogress = this.loadprogress.bind(this);
|
xhr.onprogress = this.loadprogress.bind(this);
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -21,7 +21,8 @@ const State = {
|
||||||
PARSING : 5,
|
PARSING : 5,
|
||||||
PARSED : 6,
|
PARSED : 6,
|
||||||
APPENDING : 7,
|
APPENDING : 7,
|
||||||
BUFFER_FLUSHING : 8
|
BUFFER_FLUSHING : 8,
|
||||||
|
ENDED : 9
|
||||||
};
|
};
|
||||||
|
|
||||||
class MSEMediaController extends EventHandler {
|
class MSEMediaController extends EventHandler {
|
||||||
|
@ -86,6 +87,7 @@ class MSEMediaController extends EventHandler {
|
||||||
this.mp4segments = [];
|
this.mp4segments = [];
|
||||||
this.flushRange = [];
|
this.flushRange = [];
|
||||||
this.bufferRange = [];
|
this.bufferRange = [];
|
||||||
|
this.stalled = false;
|
||||||
var frag = this.fragCurrent;
|
var frag = this.fragCurrent;
|
||||||
if (frag) {
|
if (frag) {
|
||||||
if (frag.loader) {
|
if (frag.loader) {
|
||||||
|
@ -264,13 +266,23 @@ class MSEMediaController extends EventHandler {
|
||||||
// have we reached end of VOD playlist ?
|
// have we reached end of VOD playlist ?
|
||||||
if (!levelDetails.live) {
|
if (!levelDetails.live) {
|
||||||
var mediaSource = this.mediaSource;
|
var mediaSource = this.mediaSource;
|
||||||
if (mediaSource && mediaSource.readyState === 'open') {
|
if (mediaSource) {
|
||||||
// ensure sourceBuffer are not in updating states
|
switch(mediaSource.readyState) {
|
||||||
|
case 'open':
|
||||||
var sb = this.sourceBuffer;
|
var sb = this.sourceBuffer;
|
||||||
if (!((sb.audio && sb.audio.updating) || (sb.video && sb.video.updating))) {
|
if (!((sb.audio && sb.audio.updating) || (sb.video && sb.video.updating))) {
|
||||||
logger.log('all media data available, signal endOfStream() to MediaSource');
|
logger.log('all media data available, signal endOfStream() to MediaSource and stop loading fragment');
|
||||||
//Notify the media element that it now has all of the media data
|
//Notify the media element that it now has all of the media data
|
||||||
mediaSource.endOfStream();
|
mediaSource.endOfStream();
|
||||||
|
this.state = State.ENDED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ended':
|
||||||
|
logger.log('all media data available and mediaSource ended, stop loading fragment');
|
||||||
|
this.state = State.ENDED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,6 +468,8 @@ class MSEMediaController extends EventHandler {
|
||||||
each time sourceBuffer updateend() callback will be triggered
|
each time sourceBuffer updateend() callback will be triggered
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
|
case State.ENDED:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -864,6 +878,9 @@ class MSEMediaController extends EventHandler {
|
||||||
// switch to IDLE state to load new fragment
|
// switch to IDLE state to load new fragment
|
||||||
this.state = State.IDLE;
|
this.state = State.IDLE;
|
||||||
}
|
}
|
||||||
|
} else if (this.state === State.ENDED) {
|
||||||
|
// switch to IDLE state to check for potential new fragment
|
||||||
|
this.state = State.IDLE;
|
||||||
}
|
}
|
||||||
if (this.media) {
|
if (this.media) {
|
||||||
this.lastCurrentTime = this.media.currentTime;
|
this.lastCurrentTime = this.media.currentTime;
|
||||||
|
@ -1188,17 +1205,27 @@ _checkBuffer() {
|
||||||
var currentTime = media.currentTime,
|
var currentTime = media.currentTime,
|
||||||
bufferInfo = this.bufferInfo(currentTime,0),
|
bufferInfo = this.bufferInfo(currentTime,0),
|
||||||
isPlaying = !(media.paused || media.ended || media.seeking || readyState < 3),
|
isPlaying = !(media.paused || media.ended || media.seeking || readyState < 3),
|
||||||
jumpThreshold = 0.2;
|
jumpThreshold = 0.2,
|
||||||
|
playheadMoving = currentTime > media.playbackRate*this.lastCurrentTime;
|
||||||
|
|
||||||
|
if (this.stalled && playheadMoving) {
|
||||||
|
this.stalled = false;
|
||||||
|
}
|
||||||
|
|
||||||
// check buffer upfront
|
// check buffer upfront
|
||||||
// if less than 200ms is buffered, and media is playing but playhead is not moving,
|
// if less than 200ms is buffered, and media is playing but playhead is not moving,
|
||||||
// and we have a new buffer range available upfront, let's seek to that one
|
// and we have a new buffer range available upfront, let's seek to that one
|
||||||
if(bufferInfo.len <= jumpThreshold) {
|
if(bufferInfo.len <= jumpThreshold) {
|
||||||
if(currentTime > media.playbackRate*this.lastCurrentTime || !isPlaying) {
|
if(playheadMoving || !isPlaying) {
|
||||||
// playhead moving or media not playing
|
// playhead moving or media not playing
|
||||||
jumpThreshold = 0;
|
jumpThreshold = 0;
|
||||||
} else {
|
} else {
|
||||||
|
// playhead not moving AND media playing
|
||||||
logger.log('playback seems stuck');
|
logger.log('playback seems stuck');
|
||||||
|
if(!this.stalled) {
|
||||||
|
this.hls.trigger(Event.ERROR, {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_STALLED_ERROR, fatal: false});
|
||||||
|
this.stalled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if we are below threshold, try to jump if next buffer range is close
|
// if we are below threshold, try to jump if next buffer range is close
|
||||||
if(bufferInfo.len <= jumpThreshold) {
|
if(bufferInfo.len <= jumpThreshold) {
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
switchLevel() {
|
switchLevel() {
|
||||||
this.pmtParsed = false;
|
this.pmtParsed = false;
|
||||||
this._pmtId = -1;
|
this._pmtId = -1;
|
||||||
|
this.lastAacPTS = null;
|
||||||
|
this.aacOverFlow = null;
|
||||||
this._avcTrack = {type: 'video', id :-1, sequenceNumber: 0, samples : [], len : 0, nbNalu : 0};
|
this._avcTrack = {type: 'video', id :-1, sequenceNumber: 0, samples : [], len : 0, nbNalu : 0};
|
||||||
this._aacTrack = {type: 'audio', id :-1, sequenceNumber: 0, samples : [], len : 0};
|
this._aacTrack = {type: 'audio', id :-1, sequenceNumber: 0, samples : [], len : 0};
|
||||||
this._id3Track = {type: 'id3', id :-1, sequenceNumber: 0, samples : [], len : 0};
|
this._id3Track = {type: 'id3', id :-1, sequenceNumber: 0, samples : [], len : 0};
|
||||||
|
@ -412,7 +414,7 @@
|
||||||
case 3:
|
case 3:
|
||||||
if( value === 0) {
|
if( value === 0) {
|
||||||
state = 3;
|
state = 3;
|
||||||
} else if (value === 1) {
|
} else if (value === 1 && i < len) {
|
||||||
unitType = array[i] & 0x1f;
|
unitType = array[i] & 0x1f;
|
||||||
//logger.log('find NALU @ offset:' + i + ',type:' + unitType);
|
//logger.log('find NALU @ offset:' + i + ',type:' + unitType);
|
||||||
if (lastUnitStart) {
|
if (lastUnitStart) {
|
||||||
|
@ -469,11 +471,14 @@
|
||||||
startOffset = 0,
|
startOffset = 0,
|
||||||
duration = this._duration,
|
duration = this._duration,
|
||||||
audioCodec = this.audioCodec,
|
audioCodec = this.audioCodec,
|
||||||
|
aacOverFlow = this.aacOverFlow,
|
||||||
|
lastAacPTS = this.lastAacPTS,
|
||||||
config, frameLength, frameDuration, frameIndex, offset, headerLength, stamp, len, aacSample;
|
config, frameLength, frameDuration, frameIndex, offset, headerLength, stamp, len, aacSample;
|
||||||
if (this.aacOverFlow) {
|
if (aacOverFlow) {
|
||||||
var tmp = new Uint8Array(this.aacOverFlow.byteLength + data.byteLength);
|
var tmp = new Uint8Array(aacOverFlow.byteLength + data.byteLength);
|
||||||
tmp.set(this.aacOverFlow, 0);
|
tmp.set(aacOverFlow, 0);
|
||||||
tmp.set(data, this.aacOverFlow.byteLength);
|
tmp.set(data, aacOverFlow.byteLength);
|
||||||
|
//logger.log(`AAC: append overflowing ${aacOverFlow.byteLength} bytes to beginning of new PES`);
|
||||||
data = tmp;
|
data = tmp;
|
||||||
}
|
}
|
||||||
// look for ADTS header (0xFFFx)
|
// look for ADTS header (0xFFFx)
|
||||||
|
@ -509,6 +514,17 @@
|
||||||
}
|
}
|
||||||
frameIndex = 0;
|
frameIndex = 0;
|
||||||
frameDuration = 1024 * 90000 / track.audiosamplerate;
|
frameDuration = 1024 * 90000 / track.audiosamplerate;
|
||||||
|
|
||||||
|
// if last AAC frame is overflowing, we should ensure timestamps are contiguous:
|
||||||
|
// first sample PTS should be equal to last sample PTS + frameDuration
|
||||||
|
if(aacOverFlow && lastAacPTS) {
|
||||||
|
var newPTS = lastAacPTS+frameDuration;
|
||||||
|
if(Math.abs(newPTS-pts) > 1) {
|
||||||
|
logger.log(`AAC: align PTS for overlapping frames by ${Math.round((newPTS-pts)/90)}`);
|
||||||
|
pts=newPTS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while ((offset + 5) < len) {
|
while ((offset + 5) < len) {
|
||||||
// The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header
|
// The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header
|
||||||
headerLength = (!!(data[offset + 1] & 0x01) ? 7 : 9);
|
headerLength = (!!(data[offset + 1] & 0x01) ? 7 : 9);
|
||||||
|
@ -517,11 +533,11 @@
|
||||||
(data[offset + 4] << 3) |
|
(data[offset + 4] << 3) |
|
||||||
((data[offset + 5] & 0xE0) >>> 5);
|
((data[offset + 5] & 0xE0) >>> 5);
|
||||||
frameLength -= headerLength;
|
frameLength -= headerLength;
|
||||||
stamp = Math.round(pts + frameIndex * frameDuration);
|
|
||||||
//stamp = pes.pts;
|
//stamp = pes.pts;
|
||||||
|
|
||||||
//console.log('AAC frame, offset/length/pts:' + (offset+headerLength) + '/' + frameLength + '/' + stamp.toFixed(0));
|
|
||||||
if ((frameLength > 0) && ((offset + headerLength + frameLength) <= len)) {
|
if ((frameLength > 0) && ((offset + headerLength + frameLength) <= len)) {
|
||||||
|
stamp = Math.round(pts + frameIndex * frameDuration);
|
||||||
|
//logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`);
|
||||||
aacSample = {unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp};
|
aacSample = {unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp};
|
||||||
track.samples.push(aacSample);
|
track.samples.push(aacSample);
|
||||||
track.len += frameLength;
|
track.len += frameLength;
|
||||||
|
@ -538,10 +554,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (offset < len) {
|
if (offset < len) {
|
||||||
this.aacOverFlow = data.subarray(offset, len);
|
aacOverFlow = data.subarray(offset, len);
|
||||||
|
//logger.log(`AAC: overflow detected:${len-offset}`);
|
||||||
} else {
|
} else {
|
||||||
this.aacOverFlow = null;
|
aacOverFlow = null;
|
||||||
}
|
}
|
||||||
|
this.aacOverFlow = aacOverFlow;
|
||||||
|
this.lastAacPTS = stamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
_parseID3PES(pes) {
|
_parseID3PES(pes) {
|
||||||
|
|
|
@ -37,5 +37,7 @@ export const ErrorDetails = {
|
||||||
// Identifier for a buffer append error - data: append error description
|
// Identifier for a buffer append error - data: append error description
|
||||||
BUFFER_APPEND_ERROR: 'bufferAppendError',
|
BUFFER_APPEND_ERROR: 'bufferAppendError',
|
||||||
// Identifier for a buffer appending error event - data: appending error description
|
// Identifier for a buffer appending error event - data: appending error description
|
||||||
BUFFER_APPENDING_ERROR: 'bufferAppendingError'
|
BUFFER_APPENDING_ERROR: 'bufferAppendingError',
|
||||||
|
// Identifier for a buffer stalled error event
|
||||||
|
BUFFER_STALLED_ERROR: 'bufferStalledError'
|
||||||
};
|
};
|
||||||
|
|
|
@ -295,7 +295,8 @@ class MP4Remuxer {
|
||||||
if (delta) {
|
if (delta) {
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
logger.log(`${delta} ms hole between AAC samples detected,filling it`);
|
logger.log(`${delta} ms hole between AAC samples detected,filling it`);
|
||||||
} else if (delta < 0) {
|
// if we have frame overlap, overlapping for more than half a frame duraion
|
||||||
|
} else if (delta < -12) {
|
||||||
// drop overlapping audio frames... browser will deal with it
|
// drop overlapping audio frames... browser will deal with it
|
||||||
logger.log(`${(-delta)} ms overlapping between AAC samples detected, drop frame`);
|
logger.log(`${(-delta)} ms overlapping between AAC samples detected, drop frame`);
|
||||||
track.len -= unit.byteLength;
|
track.len -= unit.byteLength;
|
||||||
|
|
|
@ -64,7 +64,7 @@ class AttrList {
|
||||||
}
|
}
|
||||||
|
|
||||||
static parseAttrList(input) {
|
static parseAttrList(input) {
|
||||||
const re = /(.+?)=((?:\".*?\")|.*?)(?:,|$)/g;
|
const re = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g;
|
||||||
var match, attrs = {};
|
var match, attrs = {};
|
||||||
while ((match = re.exec(input)) !== null) {
|
while ((match = re.exec(input)) !== null) {
|
||||||
var value = match[2], quote = '"';
|
var value = match[2], quote = '"';
|
||||||
|
|
|
@ -48,7 +48,14 @@ class XhrLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadInternal() {
|
loadInternal() {
|
||||||
var xhr = this.loader = new XMLHttpRequest();
|
var xhr;
|
||||||
|
|
||||||
|
if (typeof XDomainRequest !== 'undefined') {
|
||||||
|
xhr = this.loader = new XDomainRequest();
|
||||||
|
} else {
|
||||||
|
xhr = this.loader = new XMLHttpRequest();
|
||||||
|
}
|
||||||
|
|
||||||
xhr.onloadend = this.loadend.bind(this);
|
xhr.onloadend = this.loadend.bind(this);
|
||||||
xhr.onprogress = this.loadprogress.bind(this);
|
xhr.onprogress = this.loadprogress.bind(this);
|
||||||
|
|
||||||
|
|
|
@ -26,14 +26,14 @@
|
||||||
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
|
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
|
||||||
},
|
},
|
||||||
"main": "iron-meta.html",
|
"main": "iron-meta.html",
|
||||||
"homepage": "https://github.com/polymerelements/iron-meta",
|
"homepage": "https://github.com/PolymerElements/iron-meta",
|
||||||
"_release": "1.1.1",
|
"_release": "1.1.1",
|
||||||
"_resolution": {
|
"_resolution": {
|
||||||
"type": "version",
|
"type": "version",
|
||||||
"tag": "v1.1.1",
|
"tag": "v1.1.1",
|
||||||
"commit": "e171ee234b482219c9514e6f9551df48ef48bd9f"
|
"commit": "e171ee234b482219c9514e6f9551df48ef48bd9f"
|
||||||
},
|
},
|
||||||
"_source": "git://github.com/polymerelements/iron-meta.git",
|
"_source": "git://github.com/PolymerElements/iron-meta.git",
|
||||||
"_target": "^1.0.0",
|
"_target": "^1.0.0",
|
||||||
"_originalSource": "polymerelements/iron-meta"
|
"_originalSource": "PolymerElements/iron-meta"
|
||||||
}
|
}
|
|
@ -36,7 +36,7 @@
|
||||||
"tag": "v1.0.8",
|
"tag": "v1.0.8",
|
||||||
"commit": "e9a66727f3da0446f04956d4e4f1dcd51cdec2ff"
|
"commit": "e9a66727f3da0446f04956d4e4f1dcd51cdec2ff"
|
||||||
},
|
},
|
||||||
"_source": "git://github.com/PolymerElements/iron-selector.git",
|
"_source": "git://github.com/polymerelements/iron-selector.git",
|
||||||
"_target": "^1.0.0",
|
"_target": "^1.0.0",
|
||||||
"_originalSource": "PolymerElements/iron-selector"
|
"_originalSource": "polymerelements/iron-selector"
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue