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

update components

This commit is contained in:
Luke Pulverenti 2016-01-25 15:28:29 -05:00
parent 53c2a5fe3c
commit 24ad560e5a
16 changed files with 223 additions and 126 deletions

View file

@ -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",

View file

@ -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,36 +225,31 @@ define(['browser'], function (browser) {
var videoAudioChannels = '6'; var videoAudioChannels = '6';
profile.CodecProfiles.push({ // Handle he-aac not supported
Type: 'VideoAudio', if (!videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.5"').replace(/no/, '')) {
Codec: 'aac', profile.CodecProfiles.push({
Container: 'mkv,mov', Type: 'VideoAudio',
Conditions: [ Codec: 'aac',
{ Conditions: [
Condition: 'NotEquals', {
Property: 'AudioProfile', Condition: 'NotEquals',
Value: 'HE-AAC' Property: 'AudioProfile',
}, Value: 'HE-AAC'
{ },
Condition: 'LessThanEqual', {
Property: 'AudioChannels', Condition: 'LessThanEqual',
Value: videoAudioChannels Property: 'AudioChannels',
}, Value: videoAudioChannels
{ },
Condition: 'Equals', {
Property: 'IsSecondaryAudio', Condition: 'Equals',
Value: 'false', Property: 'IsSecondaryAudio',
IsRequired: 'false' Value: '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',

View file

@ -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,18 +55,10 @@ define(function () {
} }
window.requireCss = { window.requireCss = {
unloadPackage: function (packageName) { removeStylesheet: function (stylesheet) {
// Todo: unload css here stylesheet.parentNode.removeChild(stylesheet);
var stylesheets = document.head.querySelectorAll("link[data-package='" + packageName + "']"); removeFromLoadHistory(stylesheet.href);
for (var i = 0, length = stylesheets.length; i < length; i++) {
var stylesheet = stylesheets[i];
console.log('Unloading stylesheet: ' + stylesheet.href);
stylesheet.parentNode.removeChild(stylesheet);
removeFromLoadHistory(stylesheet.href);
}
} }
}; };

View file

@ -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",

View file

@ -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.

View file

@ -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) {
var sb = this.sourceBuffer; case 'open':
if (!(sb.audio && sb.audio.updating || sb.video && sb.video.updating)) { var sb = this.sourceBuffer;
_utilsLogger.logger.log('all media data available, signal endOfStream() to MediaSource'); if (!(sb.audio && sb.audio.updating || sb.video && sb.video.updating)) {
//Notify the media element that it now has all of the media data _utilsLogger.logger.log('all media data available, signal endOfStream() to MediaSource and stop loading fragment');
mediaSource.endOfStream(); //Notify the media element that it now has all of the media data
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,12 +6523,13 @@ 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
// drop overlapping audio frames... browser will deal with it } else if (delta < -12) {
_utilsLogger.logger.log(-delta + ' ms overlapping between AAC samples detected, drop frame'); // drop overlapping audio frames... browser will deal with it
track.len -= unit.byteLength; _utilsLogger.logger.log(-delta + ' ms overlapping between AAC samples detected, drop frame');
continue; track.len -= unit.byteLength;
} continue;
}
// set DTS to next DTS // set DTS to next DTS
ptsnorm = dtsnorm = nextAacPts; ptsnorm = dtsnorm = nextAacPts;
} }
@ -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

View file

@ -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) {
var sb = this.sourceBuffer; case 'open':
if (!((sb.audio && sb.audio.updating) || (sb.video && sb.video.updating))) { var sb = this.sourceBuffer;
logger.log('all media data available, signal endOfStream() to MediaSource'); if (!((sb.audio && sb.audio.updating) || (sb.video && sb.video.updating))) {
//Notify the media element that it now has all of the media data logger.log('all media data available, signal endOfStream() to MediaSource and stop loading fragment');
mediaSource.endOfStream(); //Notify the media element that it now has all of the media data
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) {

View file

@ -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) {

View file

@ -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'
}; };

View file

@ -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;

View file

@ -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 = '"';

View file

@ -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);

View file

@ -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"
} }

View file

@ -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"
} }