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-05-03 13:58:24 -04:00
parent 3ab597cb01
commit c2d70081cf
15 changed files with 198 additions and 204 deletions

View file

@ -1,6 +1,6 @@
{ {
"name": "hls.js", "name": "hls.js",
"version": "0.5.24", "version": "0.5.27",
"license": "Apache-2.0", "license": "Apache-2.0",
"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",
@ -16,11 +16,11 @@
"test", "test",
"tests" "tests"
], ],
"_release": "0.5.24", "_release": "0.5.27",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "v0.5.24", "tag": "v0.5.27",
"commit": "276d45148e6306e9ae06f4c9fa5778e723b78d05" "commit": "03ebf357648136a98a4b87023d3900a79a31aed2"
}, },
"_source": "git://github.com/dailymotion/hls.js.git", "_source": "git://github.com/dailymotion/hls.js.git",
"_target": "~0.5.7", "_target": "~0.5.7",

View file

@ -666,8 +666,6 @@ full list of Errors is described below:
- data: { type : ```MEDIA_ERROR```, details : ```Hls.ErrorDetails.BUFFER_FULL_ERROR```, fatal : ```false```} - data: { type : ```MEDIA_ERROR```, details : ```Hls.ErrorDetails.BUFFER_FULL_ERROR```, fatal : ```false```}
- ```Hls.ErrorDetails.BUFFER_SEEK_OVER_HOLE```raised after hls.js seeks over a buffer hole to unstuck the playback, - ```Hls.ErrorDetails.BUFFER_SEEK_OVER_HOLE```raised after hls.js seeks over a buffer hole to unstuck the playback,
- data: { type : ```MEDIA_ERROR```, details : ```Hls.ErrorDetails.BUFFER_SEEK_OVER_HOLE```, fatal : ```false```, hole : hole duration} - data: { type : ```MEDIA_ERROR```, details : ```Hls.ErrorDetails.BUFFER_SEEK_OVER_HOLE```, fatal : ```false```, hole : hole duration}
- ```Hls.ErrorDetails.BUFFER_SEEK_STUCK_IN_BUFFERED```raised after hls.js seeks to workaround a playback stuck although currentTime is buffered
- data: { type : ```MEDIA_ERROR```, details : ```Hls.ErrorDetails.BUFFER_SEEK_STUCK_IN_BUFFERED```, fatal : ```false```}
## Objects ## Objects
### Level ### Level

View file

@ -1,6 +1,6 @@
{ {
"name": "hls.js", "name": "hls.js",
"version": "0.5.24", "version": "0.5.27",
"license": "Apache-2.0", "license": "Apache-2.0",
"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",

View file

@ -660,17 +660,19 @@ var BufferController = function (_EventHandler) {
key: 'onMediaAttaching', key: 'onMediaAttaching',
value: function onMediaAttaching(data) { value: function onMediaAttaching(data) {
var media = this.media = data.media; var media = this.media = data.media;
// setup the media source if (media) {
var ms = this.mediaSource = new MediaSource(); // setup the media source
//Media Source listeners var ms = this.mediaSource = new MediaSource();
this.onmso = this.onMediaSourceOpen.bind(this); //Media Source listeners
this.onmse = this.onMediaSourceEnded.bind(this); this.onmso = this.onMediaSourceOpen.bind(this);
this.onmsc = this.onMediaSourceClose.bind(this); this.onmse = this.onMediaSourceEnded.bind(this);
ms.addEventListener('sourceopen', this.onmso); this.onmsc = this.onMediaSourceClose.bind(this);
ms.addEventListener('sourceended', this.onmse); ms.addEventListener('sourceopen', this.onmso);
ms.addEventListener('sourceclose', this.onmsc); ms.addEventListener('sourceended', this.onmse);
// link video and media Source ms.addEventListener('sourceclose', this.onmsc);
media.src = URL.createObjectURL(ms); // link video and media Source
media.src = URL.createObjectURL(ms);
}
} }
}, { }, {
key: 'onMediaDetaching', key: 'onMediaDetaching',
@ -774,23 +776,23 @@ var BufferController = function (_EventHandler) {
}, { }, {
key: 'onBufferCodecs', key: 'onBufferCodecs',
value: function onBufferCodecs(tracks) { value: function onBufferCodecs(tracks) {
var sb, trackName, track, codec, mimeType; var mediaSource = this.mediaSource;
if (!this.media) { // delay sourcebuffer creation if media source not opened yet
if (!mediaSource || mediaSource.readyState !== 'open') {
this.pendingTracks = tracks; this.pendingTracks = tracks;
return; return;
} }
if (!this.sourceBuffer) { if (!this.sourceBuffer) {
var sourceBuffer = {}, var sourceBuffer = {};
mediaSource = this.mediaSource; for (var trackName in tracks) {
for (trackName in tracks) { var track = tracks[trackName];
track = tracks[trackName];
// use levelCodec as first priority // use levelCodec as first priority
codec = track.levelCodec || track.codec; var codec = track.levelCodec || track.codec;
mimeType = track.container + ';codecs=' + codec; var mimeType = track.container + ';codecs=' + codec;
_logger.logger.log('creating sourceBuffer with mimeType:' + mimeType); _logger.logger.log('creating sourceBuffer with mimeType:' + mimeType);
sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType); var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType);
sb.addEventListener('updateend', this.onsbue); sb.addEventListener('updateend', this.onsbue);
sb.addEventListener('error', this.onsbe); sb.addEventListener('error', this.onsbe);
} }
@ -1907,11 +1909,13 @@ var StreamController = function (_EventHandler) {
}, { }, {
key: 'isBuffered', key: 'isBuffered',
value: function isBuffered(position) { value: function isBuffered(position) {
var v = this.media, var media = this.media;
buffered = v.buffered; if (media) {
for (var i = 0; i < buffered.length; i++) { var buffered = media.buffered;
if (position >= buffered.start(i) && position <= buffered.end(i)) { for (var i = 0; i < buffered.length; i++) {
return true; if (position >= buffered.start(i) && position <= buffered.end(i)) {
return true;
}
} }
} }
return false; return false;
@ -1991,9 +1995,12 @@ var StreamController = function (_EventHandler) {
key: 'immediateLevelSwitchEnd', key: 'immediateLevelSwitchEnd',
value: function immediateLevelSwitchEnd() { value: function immediateLevelSwitchEnd() {
this.immediateSwitch = false; this.immediateSwitch = false;
this.media.currentTime -= 0.0001; var media = this.media;
if (!this.previouslyPaused) { if (media && media.readyState) {
this.media.play(); media.currentTime -= 0.0001;
if (!this.previouslyPaused) {
media.play();
}
} }
} }
}, { }, {
@ -2004,45 +2011,51 @@ var StreamController = function (_EventHandler) {
we need to find the next flushable buffer range we need to find the next flushable buffer range
we should take into account new segment fetch time we should take into account new segment fetch time
*/ */
var fetchdelay, currentRange, nextRange; var media = this.media;
// increase fragment load Index to avoid frag loop loading error after buffer flush // ensure that media is defined and that metadata are available (to retrieve currentTime)
this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold; if (media && media.readyState) {
currentRange = this.getBufferRange(this.media.currentTime); var fetchdelay = void 0,
if (currentRange && currentRange.start > 1) { currentRange = void 0,
// flush buffer preceding current fragment (flush until current fragment start offset) nextRange = void 0;
// minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ... // increase fragment load Index to avoid frag loop loading error after buffer flush
this.state = State.PAUSED; this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
this.hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: currentRange.start - 1 }); currentRange = this.getBufferRange(media.currentTime);
} if (currentRange && currentRange.start > 1) {
if (!this.media.paused) { // flush buffer preceding current fragment (flush until current fragment start offset)
// add a safety delay of 1s // minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ...
var nextLevelId = this.hls.nextLoadLevel, this.state = State.PAUSED;
nextLevel = this.levels[nextLevelId], this.hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: currentRange.start - 1 });
fragLastKbps = this.fragLastKbps; }
if (fragLastKbps && this.fragCurrent) { if (!media.paused) {
fetchdelay = this.fragCurrent.duration * nextLevel.bitrate / (1000 * fragLastKbps) + 1; // add a safety delay of 1s
var nextLevelId = this.hls.nextLoadLevel,
nextLevel = this.levels[nextLevelId],
fragLastKbps = this.fragLastKbps;
if (fragLastKbps && this.fragCurrent) {
fetchdelay = this.fragCurrent.duration * nextLevel.bitrate / (1000 * fragLastKbps) + 1;
} else {
fetchdelay = 0;
}
} else { } else {
fetchdelay = 0; fetchdelay = 0;
} }
} else { //logger.log('fetchdelay:'+fetchdelay);
fetchdelay = 0; // find buffer range that will be reached once new fragment will be fetched
} nextRange = this.getBufferRange(media.currentTime + fetchdelay);
//logger.log('fetchdelay:'+fetchdelay);
// find buffer range that will be reached once new fragment will be fetched
nextRange = this.getBufferRange(this.media.currentTime + fetchdelay);
if (nextRange) {
// we can flush buffer range following this one without stalling playback
nextRange = this.followingBufferRange(nextRange);
if (nextRange) { if (nextRange) {
// if we are here, we can also cancel any loading/demuxing in progress, as they are useless // we can flush buffer range following this one without stalling playback
var fragCurrent = this.fragCurrent; nextRange = this.followingBufferRange(nextRange);
if (fragCurrent && fragCurrent.loader) { if (nextRange) {
fragCurrent.loader.abort(); // if we are here, we can also cancel any loading/demuxing in progress, as they are useless
var fragCurrent = this.fragCurrent;
if (fragCurrent && fragCurrent.loader) {
fragCurrent.loader.abort();
}
this.fragCurrent = null;
// flush position is the start position of this new buffer
this.state = State.PAUSED;
this.hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: nextRange.start, endOffset: Number.POSITIVE_INFINITY });
} }
this.fragCurrent = null;
// flush position is the start position of this new buffer
this.state = State.PAUSED;
this.hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: nextRange.start, endOffset: Number.POSITIVE_INFINITY });
} }
} }
} }
@ -2271,7 +2284,10 @@ var StreamController = function (_EventHandler) {
} }
this.pendingAppending = 0; this.pendingAppending = 0;
_logger.logger.log('Demuxing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + level); _logger.logger.log('Demuxing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + level);
this.demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata); var demuxer = this.demuxer;
if (demuxer) {
demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata);
}
} }
} }
this.fragLoadError = 0; this.fragLoadError = 0;
@ -2533,7 +2549,6 @@ var StreamController = function (_EventHandler) {
// check buffer upfront // check buffer upfront
// if less than jumpThreshold second is buffered, and media is expected to play but playhead is not moving, // if less than jumpThreshold second is buffered, and media is expected to play 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
var configSeekHoleNudgeDuration = this.config.seekHoleNudgeDuration;
if (expectedPlaying && bufferInfo.len <= jumpThreshold) { if (expectedPlaying && bufferInfo.len <= jumpThreshold) {
if (playheadMoving) { if (playheadMoving) {
// playhead moving // playhead moving
@ -2547,7 +2562,7 @@ var StreamController = function (_EventHandler) {
this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_STALLED_ERROR, fatal: false }); this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_STALLED_ERROR, fatal: false });
this.stalled = true; this.stalled = true;
} else { } else {
this.seekHoleNudgeDuration += configSeekHoleNudgeDuration; this.seekHoleNudgeDuration += this.config.SeekHoleNudgeDuration;
} }
} }
// 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
@ -2564,30 +2579,10 @@ var StreamController = function (_EventHandler) {
this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_SEEK_OVER_HOLE, fatal: false, hole: hole }); this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_SEEK_OVER_HOLE, fatal: false, hole: hole });
} }
} }
// in any case reset stalledInBuffered
this.stalledInBuffered = 0;
} else { } else {
if (targetSeekPosition && media.currentTime !== targetSeekPosition) { if (targetSeekPosition && media.currentTime !== targetSeekPosition) {
_logger.logger.log('adjust currentTime from ' + media.currentTime + ' to ' + targetSeekPosition); _logger.logger.log('adjust currentTime from ' + media.currentTime + ' to ' + targetSeekPosition);
media.currentTime = targetSeekPosition; media.currentTime = targetSeekPosition;
} else if (expectedPlaying && !playheadMoving) {
// if we are in this condition, it means that currentTime is in a buffered area, but playhead is not moving
// if that happens, we wait for a couple of cycle (config.stalledInBufferedNudgeThreshold), then we nudge
// media.currentTime to try to recover that situation.
if (this.stalledInBuffered !== undefined) {
this.stalledInBuffered++;
} else {
this.stalledInBuffered = 1;
}
if (this.stalledInBuffered >= this.config.stalledInBufferedNudgeThreshold) {
_logger.logger.log('playback stuck @ ' + media.currentTime + ', in buffered area, nudge currentTime by ' + configSeekHoleNudgeDuration);
this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_SEEK_STUCK_IN_BUFFERED, fatal: false });
media.currentTime += configSeekHoleNudgeDuration;
this.stalledInBuffered = 0;
}
} else {
// currentTime is buffered, playhead is moving or playback not expected... everything is fine
this.stalledInBuffered = 0;
} }
} }
} }
@ -5196,8 +5191,6 @@ var ErrorDetails = exports.ErrorDetails = {
BUFFER_FULL_ERROR: 'bufferFullError', BUFFER_FULL_ERROR: 'bufferFullError',
// Identifier for a buffer seek over hole event // Identifier for a buffer seek over hole event
BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole', BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole',
// Identifier for a seek triggered to workaround a playback stuck although currentTime is buffered
BUFFER_SEEK_STUCK_IN_BUFFERED: 'bufferSeekStuckInBuffered',
// Identifier for an internal exception happening inside hls.js while handling an event // Identifier for an internal exception happening inside hls.js while handling an event
INTERNAL_EXCEPTION: 'internalException' INTERNAL_EXCEPTION: 'internalException'
}; };
@ -5467,6 +5460,12 @@ var BufferHelper = function () {
buffered2.push(buffered[i]); buffered2.push(buffered[i]);
} }
} }
// in case current position is located before buffered time ranges, report area as not buffered
if (buffered2.length && pos < buffered2[0].start) {
return { len: 0, start: pos, end: pos, nextStart: buffered2[0].start };
}
for (i = 0, bufferLen = 0, bufferStart = bufferEnd = pos; i < buffered2.length; i++) { for (i = 0, bufferLen = 0, bufferStart = bufferEnd = pos; i < buffered2.length; i++) {
var start = buffered2[i].start, var start = buffered2[i].start,
end = buffered2[i].end; end = buffered2[i].end;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
{ {
"name": "hls.js", "name": "hls.js",
"version": "0.5.24", "version": "0.5.27",
"license": "Apache-2.0", "license": "Apache-2.0",
"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",

View file

@ -30,18 +30,20 @@ class BufferController extends EventHandler {
} }
onMediaAttaching(data) { onMediaAttaching(data) {
var media = this.media = data.media; let media = this.media = data.media;
// setup the media source if (media) {
var ms = this.mediaSource = new MediaSource(); // setup the media source
//Media Source listeners var ms = this.mediaSource = new MediaSource();
this.onmso = this.onMediaSourceOpen.bind(this); //Media Source listeners
this.onmse = this.onMediaSourceEnded.bind(this); this.onmso = this.onMediaSourceOpen.bind(this);
this.onmsc = this.onMediaSourceClose.bind(this); this.onmse = this.onMediaSourceEnded.bind(this);
ms.addEventListener('sourceopen', this.onmso); this.onmsc = this.onMediaSourceClose.bind(this);
ms.addEventListener('sourceended', this.onmse); ms.addEventListener('sourceopen', this.onmso);
ms.addEventListener('sourceclose', this.onmsc); ms.addEventListener('sourceended', this.onmse);
// link video and media Source ms.addEventListener('sourceclose', this.onmsc);
media.src = URL.createObjectURL(ms); // link video and media Source
media.src = URL.createObjectURL(ms);
}
} }
onMediaDetaching() { onMediaDetaching() {
@ -139,22 +141,23 @@ class BufferController extends EventHandler {
} }
onBufferCodecs(tracks) { onBufferCodecs(tracks) {
var sb,trackName,track, codec, mimeType; let mediaSource = this.mediaSource;
if (!this.media) { // delay sourcebuffer creation if media source not opened yet
if(!mediaSource || mediaSource.readyState !== 'open') {
this.pendingTracks = tracks; this.pendingTracks = tracks;
return; return;
} }
if (!this.sourceBuffer) { if (!this.sourceBuffer) {
var sourceBuffer = {}, mediaSource = this.mediaSource; let sourceBuffer = {};
for (trackName in tracks) { for (let trackName in tracks) {
track = tracks[trackName]; let track = tracks[trackName];
// use levelCodec as first priority // use levelCodec as first priority
codec = track.levelCodec || track.codec; let codec = track.levelCodec || track.codec;
mimeType = `${track.container};codecs=${codec}`; let mimeType = `${track.container};codecs=${codec}`;
logger.log(`creating sourceBuffer with mimeType:${mimeType}`); logger.log(`creating sourceBuffer with mimeType:${mimeType}`);
sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType); let sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType);
sb.addEventListener('updateend', this.onsbue); sb.addEventListener('updateend', this.onsbue);
sb.addEventListener('error', this.onsbe); sb.addEventListener('error', this.onsbe);
} }

View file

@ -426,10 +426,13 @@ class StreamController extends EventHandler {
} }
isBuffered(position) { isBuffered(position) {
var v = this.media, buffered = v.buffered; let media = this.media;
for (var i = 0; i < buffered.length; i++) { if (media) {
if (position >= buffered.start(i) && position <= buffered.end(i)) { let buffered = media.buffered;
return true; for (let i = 0; i < buffered.length; i++) {
if (position >= buffered.start(i) && position <= buffered.end(i)) {
return true;
}
} }
} }
return false; return false;
@ -500,9 +503,12 @@ class StreamController extends EventHandler {
*/ */
immediateLevelSwitchEnd() { immediateLevelSwitchEnd() {
this.immediateSwitch = false; this.immediateSwitch = false;
this.media.currentTime -= 0.0001; let media = this.media;
if (!this.previouslyPaused) { if (media && media.readyState) {
this.media.play(); media.currentTime -= 0.0001;
if (!this.previouslyPaused) {
media.play();
}
} }
} }
@ -512,43 +518,47 @@ class StreamController extends EventHandler {
we need to find the next flushable buffer range we need to find the next flushable buffer range
we should take into account new segment fetch time we should take into account new segment fetch time
*/ */
var fetchdelay, currentRange, nextRange; let media = this.media;
// increase fragment load Index to avoid frag loop loading error after buffer flush // ensure that media is defined and that metadata are available (to retrieve currentTime)
this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold; if (media && media.readyState) {
currentRange = this.getBufferRange(this.media.currentTime); let fetchdelay, currentRange, nextRange;
if (currentRange && currentRange.start > 1) { // increase fragment load Index to avoid frag loop loading error after buffer flush
// flush buffer preceding current fragment (flush until current fragment start offset) this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
// minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ... currentRange = this.getBufferRange(media.currentTime);
this.state = State.PAUSED; if (currentRange && currentRange.start > 1) {
this.hls.trigger(Event.BUFFER_FLUSHING, {startOffset: 0, endOffset: currentRange.start - 1}); // flush buffer preceding current fragment (flush until current fragment start offset)
} // minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ...
if (!this.media.paused) { this.state = State.PAUSED;
// add a safety delay of 1s this.hls.trigger(Event.BUFFER_FLUSHING, {startOffset: 0, endOffset: currentRange.start - 1});
var nextLevelId = this.hls.nextLoadLevel,nextLevel = this.levels[nextLevelId], fragLastKbps = this.fragLastKbps; }
if (fragLastKbps && this.fragCurrent) { if (!media.paused) {
fetchdelay = this.fragCurrent.duration * nextLevel.bitrate / (1000 * fragLastKbps) + 1; // add a safety delay of 1s
var nextLevelId = this.hls.nextLoadLevel,nextLevel = this.levels[nextLevelId], fragLastKbps = this.fragLastKbps;
if (fragLastKbps && this.fragCurrent) {
fetchdelay = this.fragCurrent.duration * nextLevel.bitrate / (1000 * fragLastKbps) + 1;
} else {
fetchdelay = 0;
}
} else { } else {
fetchdelay = 0; fetchdelay = 0;
} }
} else { //logger.log('fetchdelay:'+fetchdelay);
fetchdelay = 0; // find buffer range that will be reached once new fragment will be fetched
} nextRange = this.getBufferRange(media.currentTime + fetchdelay);
//logger.log('fetchdelay:'+fetchdelay);
// find buffer range that will be reached once new fragment will be fetched
nextRange = this.getBufferRange(this.media.currentTime + fetchdelay);
if (nextRange) {
// we can flush buffer range following this one without stalling playback
nextRange = this.followingBufferRange(nextRange);
if (nextRange) { if (nextRange) {
// if we are here, we can also cancel any loading/demuxing in progress, as they are useless // we can flush buffer range following this one without stalling playback
var fragCurrent = this.fragCurrent; nextRange = this.followingBufferRange(nextRange);
if (fragCurrent && fragCurrent.loader) { if (nextRange) {
fragCurrent.loader.abort(); // if we are here, we can also cancel any loading/demuxing in progress, as they are useless
var fragCurrent = this.fragCurrent;
if (fragCurrent && fragCurrent.loader) {
fragCurrent.loader.abort();
}
this.fragCurrent = null;
// flush position is the start position of this new buffer
this.state = State.PAUSED;
this.hls.trigger(Event.BUFFER_FLUSHING, {startOffset: nextRange.start, endOffset: Number.POSITIVE_INFINITY});
} }
this.fragCurrent = null;
// flush position is the start position of this new buffer
this.state = State.PAUSED;
this.hls.trigger(Event.BUFFER_FLUSHING, {startOffset: nextRange.start, endOffset: Number.POSITIVE_INFINITY});
} }
} }
} }
@ -769,7 +779,10 @@ class StreamController extends EventHandler {
} }
this.pendingAppending = 0; this.pendingAppending = 0;
logger.log(`Demuxing ${sn} of [${details.startSN} ,${details.endSN}],level ${level}`); logger.log(`Demuxing ${sn} of [${details.startSN} ,${details.endSN}],level ${level}`);
this.demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata); let demuxer = this.demuxer;
if (demuxer) {
demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata);
}
} }
} }
this.fragLoadError = 0; this.fragLoadError = 0;
@ -1018,7 +1031,6 @@ _checkBuffer() {
// check buffer upfront // check buffer upfront
// if less than jumpThreshold second is buffered, and media is expected to play but playhead is not moving, // if less than jumpThreshold second is buffered, and media is expected to play 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
let configSeekHoleNudgeDuration = this.config.seekHoleNudgeDuration;
if(expectedPlaying && bufferInfo.len <= jumpThreshold) { if(expectedPlaying && bufferInfo.len <= jumpThreshold) {
if(playheadMoving) { if(playheadMoving) {
// playhead moving // playhead moving
@ -1032,7 +1044,7 @@ _checkBuffer() {
this.hls.trigger(Event.ERROR, {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_STALLED_ERROR, fatal: false}); this.hls.trigger(Event.ERROR, {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_STALLED_ERROR, fatal: false});
this.stalled = true; this.stalled = true;
} else { } else {
this.seekHoleNudgeDuration += configSeekHoleNudgeDuration; this.seekHoleNudgeDuration += this.config.SeekHoleNudgeDuration;
} }
} }
// 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
@ -1051,30 +1063,10 @@ _checkBuffer() {
this.hls.trigger(Event.ERROR, {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_SEEK_OVER_HOLE, fatal: false, hole : hole}); this.hls.trigger(Event.ERROR, {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_SEEK_OVER_HOLE, fatal: false, hole : hole});
} }
} }
// in any case reset stalledInBuffered
this.stalledInBuffered = 0;
} else { } else {
if (targetSeekPosition && media.currentTime !== targetSeekPosition) { if (targetSeekPosition && media.currentTime !== targetSeekPosition) {
logger.log(`adjust currentTime from ${media.currentTime} to ${targetSeekPosition}`); logger.log(`adjust currentTime from ${media.currentTime} to ${targetSeekPosition}`);
media.currentTime = targetSeekPosition; media.currentTime = targetSeekPosition;
} else if (expectedPlaying && !playheadMoving) {
// if we are in this condition, it means that currentTime is in a buffered area, but playhead is not moving
// if that happens, we wait for a couple of cycle (config.stalledInBufferedNudgeThreshold), then we nudge
// media.currentTime to try to recover that situation.
if (this.stalledInBuffered !== undefined) {
this.stalledInBuffered++;
} else {
this.stalledInBuffered = 1;
}
if (this.stalledInBuffered >= this.config.stalledInBufferedNudgeThreshold) {
logger.log(`playback stuck @ ${media.currentTime}, in buffered area, nudge currentTime by ${configSeekHoleNudgeDuration}`);
this.hls.trigger(Event.ERROR, {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_SEEK_STUCK_IN_BUFFERED, fatal: false});
media.currentTime+=configSeekHoleNudgeDuration;
this.stalledInBuffered = 0;
}
} else {
// currentTime is buffered, playhead is moving or playback not expected... everything is fine
this.stalledInBuffered = 0;
} }
} }
} }

View file

@ -46,8 +46,6 @@ export const ErrorDetails = {
BUFFER_FULL_ERROR: 'bufferFullError', BUFFER_FULL_ERROR: 'bufferFullError',
// Identifier for a buffer seek over hole event // Identifier for a buffer seek over hole event
BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole', BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole',
// Identifier for a seek triggered to workaround a playback stuck although currentTime is buffered
BUFFER_SEEK_STUCK_IN_BUFFERED : 'bufferSeekStuckInBuffered',
// Identifier for an internal exception happening inside hls.js while handling an event // Identifier for an internal exception happening inside hls.js while handling an event
INTERNAL_EXCEPTION: 'internalException' INTERNAL_EXCEPTION: 'internalException'
}; };

View file

@ -55,6 +55,12 @@ class BufferHelper {
buffered2.push(buffered[i]); buffered2.push(buffered[i]);
} }
} }
// in case current position is located before buffered time ranges, report area as not buffered
if (buffered2.length && pos < buffered2[0].start) {
return {len: 0, start: pos, end: pos, nextStart : buffered2[0].start};
}
for (i = 0, bufferLen = 0, bufferStart = bufferEnd = pos; i < buffered2.length; i++) { for (i = 0, bufferLen = 0, bufferStart = bufferEnd = pos; i < buffered2.length; i++) {
var start = buffered2[i].start, var start = buffered2[i].start,
end = buffered2[i].end; end = buffered2[i].end;

View file

@ -1,6 +1,6 @@
{ {
"name": "iron-overlay-behavior", "name": "iron-overlay-behavior",
"version": "1.6.4", "version": "1.6.5",
"license": "http://polymer.github.io/LICENSE.txt", "license": "http://polymer.github.io/LICENSE.txt",
"description": "Provides a behavior for making an element an overlay", "description": "Provides a behavior for making an element an overlay",
"private": true, "private": true,
@ -35,11 +35,11 @@
}, },
"ignore": [], "ignore": [],
"homepage": "https://github.com/polymerelements/iron-overlay-behavior", "homepage": "https://github.com/polymerelements/iron-overlay-behavior",
"_release": "1.6.4", "_release": "1.6.5",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "v1.6.4", "tag": "v1.6.5",
"commit": "983654132fd8281c3da07d79eea8a0f5b28e7a4f" "commit": "19311400fe42fe3992fb8925dfd2582c63c67d82"
}, },
"_source": "git://github.com/polymerelements/iron-overlay-behavior.git", "_source": "git://github.com/polymerelements/iron-overlay-behavior.git",
"_target": "^1.0.0", "_target": "^1.0.0",

View file

@ -1,6 +1,6 @@
{ {
"name": "iron-overlay-behavior", "name": "iron-overlay-behavior",
"version": "1.6.4", "version": "1.6.5",
"license": "http://polymer.github.io/LICENSE.txt", "license": "http://polymer.github.io/LICENSE.txt",
"description": "Provides a behavior for making an element an overlay", "description": "Provides a behavior for making an element an overlay",
"private": true, "private": true,

View file

@ -591,17 +591,15 @@ context. You should place this element as a child of `<body>` whenever possible.
* Fired when the overlay is canceled, but before it is closed. * Fired when the overlay is canceled, but before it is closed.
* @event iron-overlay-canceled * @event iron-overlay-canceled
* @param {Event} event The closing of the overlay can be prevented * @param {Event} event The closing of the overlay can be prevented
* by calling `event.preventDefault()`. * by calling `event.preventDefault()`. The `event.detail` is the original event that
* @param {Event} event.detail It is the original event that originated * originated the canceling (e.g. ESC keyboard event or click event outside the overlay).
* the canceling (e.g. ESC keyboard event or click event outside the overlay).
*/ */
/** /**
* Fired after the overlay closes. * Fired after the overlay closes.
* @event iron-overlay-closed * @event iron-overlay-closed
* @param {Event} event The event * @param {Event} event The `event.detail` is the `closingReason` property
* @param {Object} event.detail It is the `closingReason` property (contains * (contains `canceled`, whether the overlay was canceled).
* `canceled`, whether the overlay was canceled).
*/ */
})(); })();

View file

@ -54,7 +54,7 @@
"tag": "v1.1.11", "tag": "v1.1.11",
"commit": "8cfe5c5bf8c2e40d243443d046a94b6fe371983c" "commit": "8cfe5c5bf8c2e40d243443d046a94b6fe371983c"
}, },
"_source": "git://github.com/polymerelements/paper-input.git", "_source": "git://github.com/PolymerElements/paper-input.git",
"_target": "^1.0.9", "_target": "^1.0.0",
"_originalSource": "polymerelements/paper-input" "_originalSource": "PolymerElements/paper-input"
} }