From 479a850d9edebd92536615e1209b477a97b35f99 Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 15:33:32 -0400 Subject: [PATCH 01/12] add init props for errors --- src/controller/level-controller.js | 56 +++++++++++++++++++----------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 7325ebe0ba3..5e31af7c1d5 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -57,25 +57,28 @@ class LevelController extends EventHandler { } onManifestLoaded(data) { - var levels0 = [], - levels = [], + let levels0 = [], + levels = [], bitrateStart, - bitrateSet = {}, + bitrateSet = {}, videoCodecFound = false, audioCodecFound = false, - hls = this.hls, - brokenmp4inmp3 = /chrome|firefox/.test(navigator.userAgent.toLowerCase()); + brokenmp4inmp3 = /chrome|firefox/.test(navigator.userAgent.toLowerCase()); - // regroup redundant level together + // regroup redundant levels together data.levels.forEach(level => { - if(level.videoCodec) { + level.loadError = 0; + level.fragmentError = false; + + if (level.videoCodec) { videoCodecFound = true; } - // erase audio codec info if browser does not support mp4a.40.34. demuxer will autodetect codec and fallback to mpeg/audio - if(brokenmp4inmp3 && level.audioCodec && level.audioCodec.indexOf('mp4a.40.34') !== -1) { + // erase audio codec info if browser does not support mp4a.40.34. + // demuxer will autodetect codec and fallback to mpeg/audio + if (brokenmp4inmp3 && level.audioCodec && level.audioCodec.indexOf('mp4a.40.34') !== -1) { level.audioCodec = undefined; } - if(level.audioCodec || (level.attrs && level.attrs.AUDIO)) { + if (level.audioCodec || (level.attrs && level.attrs.AUDIO)) { audioCodecFound = true; } let redundantLevelId = bitrateSet[level.bitrate]; @@ -90,23 +93,22 @@ class LevelController extends EventHandler { }); // remove audio-only level if we also have levels with audio+video codecs signalled - if(videoCodecFound && audioCodecFound) { + if (videoCodecFound && audioCodecFound) { levels0.forEach(level => { - if(level.videoCodec) { + if (level.videoCodec) { levels.push(level); } }); } else { levels = levels0; } - // only keep level with supported audio/video codecs - levels = levels.filter(function(level) { - let audioCodec = level.audioCodec, videoCodec = level.videoCodec; - return (!audioCodec || isCodecSupportedInMp4(audioCodec)) && - (!videoCodec || isCodecSupportedInMp4(videoCodec)); + + // only keep levels with supported audio/video codecs + levels = levels.filter(({audioCodec, videoCodec}) => { + return (!audioCodec || isCodecSupportedInMp4(audioCodec)) && (!videoCodec || isCodecSupportedInMp4(videoCodec)); }); - if(levels.length) { + if (levels.length > 0) { // start bitrate is the first bitrate of the manifest bitrateStart = levels[0].bitrate; // sort level on bitrate @@ -122,11 +124,23 @@ class LevelController extends EventHandler { break; } } - hls.trigger(Event.MANIFEST_PARSED, {levels: levels, firstLevel: this._firstLevel, stats: data.stats, audio : audioCodecFound, video : videoCodecFound, altAudio : data.audioTracks.length > 0}); + this.hls.trigger(Event.MANIFEST_PARSED, { + levels : levels, + firstLevel: this._firstLevel, + stats : data.stats, + audio : audioCodecFound, + video : videoCodecFound, + altAudio : data.audioTracks.length > 0 + }); } else { - hls.trigger(Event.ERROR, {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR, fatal: true, url: hls.url, reason: 'no level with compatible codecs found in manifest'}); + this.hls.trigger(Event.ERROR, { + type : ErrorTypes.MEDIA_ERROR, + details: ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR, + fatal : true, + url : this.hls.url, + reason : 'no level with compatible codecs found in manifest' + }); } - return; } get levels() { From 6c6604849e4b2fa46f72aaeb45071b5d138f7f8d Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 16:12:07 -0400 Subject: [PATCH 02/12] remove redundant variable for levels --- src/controller/level-controller.js | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 5e31af7c1d5..10ed408a88c 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -57,10 +57,10 @@ class LevelController extends EventHandler { } onManifestLoaded(data) { - let levels0 = [], - levels = [], + let levels = [], bitrateStart, - bitrateSet = {}, + levelSet = {}, + levelFromSet = null, videoCodecFound = false, audioCodecFound = false, brokenmp4inmp3 = /chrome|firefox/.test(navigator.userAgent.toLowerCase()); @@ -81,26 +81,22 @@ class LevelController extends EventHandler { if (level.audioCodec || (level.attrs && level.attrs.AUDIO)) { audioCodecFound = true; } - let redundantLevelId = bitrateSet[level.bitrate]; - if (redundantLevelId === undefined) { - bitrateSet[level.bitrate] = levels0.length; + + levelFromSet = levelSet[level.bitrate]; + + if (levelFromSet === undefined) { level.url = [level.url]; level.urlId = 0; - levels0.push(level); + levelSet[level.bitrate] = level; + levels.push(level); } else { - levels0[redundantLevelId].url.push(level.url); + levelFromSet.url.push(level.url); } }); // remove audio-only level if we also have levels with audio+video codecs signalled - if (videoCodecFound && audioCodecFound) { - levels0.forEach(level => { - if (level.videoCodec) { - levels.push(level); - } - }); - } else { - levels = levels0; + if (videoCodecFound === true && audioCodecFound === true) { + levels = levels.filter(({videoCodec}) => !!videoCodec); } // only keep levels with supported audio/video codecs From fe9bfaab1d07dc491101cfeb76134d53b8a4f774 Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 16:15:21 -0400 Subject: [PATCH 03/12] rename chrome/firefox check variable --- src/controller/level-controller.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 10ed408a88c..895378f3818 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -63,7 +63,7 @@ class LevelController extends EventHandler { levelFromSet = null, videoCodecFound = false, audioCodecFound = false, - brokenmp4inmp3 = /chrome|firefox/.test(navigator.userAgent.toLowerCase()); + chromeOrFirefox = /chrome|firefox/.test(navigator.userAgent.toLowerCase()); // regroup redundant levels together data.levels.forEach(level => { @@ -75,10 +75,9 @@ class LevelController extends EventHandler { } // erase audio codec info if browser does not support mp4a.40.34. // demuxer will autodetect codec and fallback to mpeg/audio - if (brokenmp4inmp3 && level.audioCodec && level.audioCodec.indexOf('mp4a.40.34') !== -1) { + if (chromeOrFirefox === true && level.audioCodec && level.audioCodec.indexOf('mp4a.40.34') !== -1) { level.audioCodec = undefined; - } - if (level.audioCodec || (level.attrs && level.attrs.AUDIO)) { + } else if (level.audioCodec || (level.attrs && level.attrs.AUDIO)) { audioCodecFound = true; } From 263312302cbfd3b0e7a54572e6dd52c2a30a29d5 Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 16:18:18 -0400 Subject: [PATCH 04/12] change formatting for error handler --- src/controller/level-controller.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 895378f3818..590507d0377 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -226,7 +226,7 @@ class LevelController extends EventHandler { } onError(data) { - if(data.fatal) { + if (data.fatal) { if (data.type === ErrorTypes.NETWORK_ERROR) { this.cleanTimer(); } @@ -235,14 +235,14 @@ class LevelController extends EventHandler { let details = data.details, hls = this.hls, levelId, level, levelError = false; // try to recover not fatal errors - switch(details) { + switch (details) { case ErrorDetails.FRAG_LOAD_ERROR: case ErrorDetails.FRAG_LOAD_TIMEOUT: case ErrorDetails.FRAG_LOOP_LOADING_ERROR: case ErrorDetails.KEY_LOAD_ERROR: case ErrorDetails.KEY_LOAD_TIMEOUT: - levelId = data.frag.level; - break; + levelId = data.frag.level; + break; case ErrorDetails.LEVEL_LOAD_ERROR: case ErrorDetails.LEVEL_LOAD_TIMEOUT: levelId = data.context.level; @@ -260,7 +260,7 @@ class LevelController extends EventHandler { */ if (levelId !== undefined) { level = this._levels[levelId]; - if(!level.loadError) { + if (!level.loadError) { level.loadError = 1; } else { level.loadError++; @@ -268,7 +268,7 @@ class LevelController extends EventHandler { // if any redundant streams available and if we haven't try them all (level.loadError is reseted on successful frag/level load. // if level.loadError reaches nbRedundantLevel it means that we tried them all, no hope => let's switch down const nbRedundantLevel = level.url.length; - if (nbRedundantLevel > 1 && level.loadError < nbRedundantLevel) { + if (nbRedundantLevel > 1 && level.loadError < nbRedundantLevel) { level.urlId = (level.urlId + 1) % nbRedundantLevel; level.details = undefined; logger.warn(`level controller,${details} for level ${levelId}: switching to redundant stream id ${level.urlId}`); @@ -277,23 +277,22 @@ class LevelController extends EventHandler { let recoverable = ((this._manualLevel === -1) && levelId); if (recoverable) { logger.warn(`level controller,${details}: switch-down for next fragment`); - hls.nextAutoLevel = Math.max(0,levelId-1); - } else if(level && level.details && level.details.live) { + hls.nextAutoLevel = Math.max(0, levelId - 1); + } else if (level && level.details && level.details.live) { logger.warn(`level controller,${details} on live stream, discard`); if (levelError) { // reset this._level so that another call to set level() will retrigger a frag load this._level = undefined; } // other errors are handled by stream controller - } else if (details === ErrorDetails.LEVEL_LOAD_ERROR || - details === ErrorDetails.LEVEL_LOAD_TIMEOUT) { - let media = hls.media, - // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end - mediaBuffered = media && BufferHelper.isBuffered(media,media.currentTime) && BufferHelper.isBuffered(media,media.currentTime+0.5); + } else if (levelError === true) { + let media = hls.media, + // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end + mediaBuffered = media && BufferHelper.isBuffered(media, media.currentTime) && BufferHelper.isBuffered(media, media.currentTime + 0.5); if (mediaBuffered) { let retryDelay = hls.config.levelLoadingRetryDelay; logger.warn(`level controller,${details}, but media buffered, retry in ${retryDelay}ms`); - this.timer = setTimeout(this.ontick,retryDelay); + this.timer = setTimeout(this.ontick, retryDelay); // boolean used to inform stream controller not to switch back to IDLE on non fatal error data.levelRetry = true; } else { From e4d5aec22f56b1b581c9e4c529e554d72df5b435 Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 16:23:37 -0400 Subject: [PATCH 05/12] remove redundant hls variable --- src/controller/level-controller.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 590507d0377..2444f2268c0 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -233,7 +233,7 @@ class LevelController extends EventHandler { return; } - let details = data.details, hls = this.hls, levelId, level, levelError = false; + let details = data.details, levelId, level, levelError = false; // try to recover not fatal errors switch (details) { case ErrorDetails.FRAG_LOAD_ERROR: @@ -277,7 +277,7 @@ class LevelController extends EventHandler { let recoverable = ((this._manualLevel === -1) && levelId); if (recoverable) { logger.warn(`level controller,${details}: switch-down for next fragment`); - hls.nextAutoLevel = Math.max(0, levelId - 1); + this.hls.nextAutoLevel = Math.max(0, levelId - 1); } else if (level && level.details && level.details.live) { logger.warn(`level controller,${details} on live stream, discard`); if (levelError) { @@ -286,11 +286,11 @@ class LevelController extends EventHandler { } // other errors are handled by stream controller } else if (levelError === true) { - let media = hls.media, + let media = this.hls.media, // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end mediaBuffered = media && BufferHelper.isBuffered(media, media.currentTime) && BufferHelper.isBuffered(media, media.currentTime + 0.5); if (mediaBuffered) { - let retryDelay = hls.config.levelLoadingRetryDelay; + let retryDelay = this.hls.config.levelLoadingRetryDelay; logger.warn(`level controller,${details}, but media buffered, retry in ${retryDelay}ms`); this.timer = setTimeout(this.ontick, retryDelay); // boolean used to inform stream controller not to switch back to IDLE on non fatal error From 38b6e556ed1c48ca1a94cc3b76a915e05d3d7f57 Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 16:37:29 -0400 Subject: [PATCH 06/12] set fragment error on the level --- src/controller/level-controller.js | 58 +++++++++++++++--------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 2444f2268c0..9ea64d48672 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -226,14 +226,17 @@ class LevelController extends EventHandler { } onError(data) { - if (data.fatal) { + if (data.fatal === true) { if (data.type === ErrorTypes.NETWORK_ERROR) { this.cleanTimer(); } return; } - let details = data.details, levelId, level, levelError = false; + let details = data.details, levelError = false, fragmentError = false; + let levelIndex, level; + let {config, media} = this.hls; + // try to recover not fatal errors switch (details) { case ErrorDetails.FRAG_LOAD_ERROR: @@ -241,58 +244,53 @@ class LevelController extends EventHandler { case ErrorDetails.FRAG_LOOP_LOADING_ERROR: case ErrorDetails.KEY_LOAD_ERROR: case ErrorDetails.KEY_LOAD_TIMEOUT: - levelId = data.frag.level; + levelIndex = data.frag.level; + fragmentError = true; break; case ErrorDetails.LEVEL_LOAD_ERROR: case ErrorDetails.LEVEL_LOAD_TIMEOUT: - levelId = data.context.level; + levelIndex = data.context.level; levelError = true; break; case ErrorDetails.REMUX_ALLOC_ERROR: - levelId = data.level; - break; - default: + levelIndex = data.level; break; } /* try to switch to a redundant stream if any available. * if no redundant stream available, emergency switch down (if in auto mode and current level not 0) * otherwise, we cannot recover this network error ... */ - if (levelId !== undefined) { - level = this._levels[levelId]; - if (!level.loadError) { - level.loadError = 1; - } else { - level.loadError++; - } + if (levelIndex !== undefined) { + level = this._levels[levelIndex]; + level.loadError++; + level.fragmentError = fragmentError; + // if any redundant streams available and if we haven't try them all (level.loadError is reseted on successful frag/level load. - // if level.loadError reaches nbRedundantLevel it means that we tried them all, no hope => let's switch down - const nbRedundantLevel = level.url.length; - if (nbRedundantLevel > 1 && level.loadError < nbRedundantLevel) { - level.urlId = (level.urlId + 1) % nbRedundantLevel; + // if level.loadError reaches redundantLevels it means that we tried them all, no hope => let's switch down + const redundantLevels = level.url.length; + + if (redundantLevels > 1 && level.loadError < redundantLevels) { + level.urlId = (level.urlId + 1) % redundantLevels; level.details = undefined; - logger.warn(`level controller,${details} for level ${levelId}: switching to redundant stream id ${level.urlId}`); + logger.warn(`level controller,${details} for level ${levelIndex}: switching to redundant stream id ${level.urlId}`); } else { // we could try to recover if in auto mode and current level not lowest level (0) - let recoverable = ((this._manualLevel === -1) && levelId); - if (recoverable) { + if (this._manualLevel === -1 && levelIndex !== 0) { logger.warn(`level controller,${details}: switch-down for next fragment`); - this.hls.nextAutoLevel = Math.max(0, levelId - 1); + this.hls.nextAutoLevel = Math.max(0, levelIndex - 1); } else if (level && level.details && level.details.live) { logger.warn(`level controller,${details} on live stream, discard`); - if (levelError) { - // reset this._level so that another call to set level() will retrigger a frag load + if (levelError === true) { + // reset this._level so that another call to set level() will trigger again a frag load this._level = undefined; } // other errors are handled by stream controller } else if (levelError === true) { - let media = this.hls.media, - // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end - mediaBuffered = media && BufferHelper.isBuffered(media, media.currentTime) && BufferHelper.isBuffered(media, media.currentTime + 0.5); + // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end + let mediaBuffered = media && BufferHelper.isBuffered(media, media.currentTime) && BufferHelper.isBuffered(media, media.currentTime + 0.5); if (mediaBuffered) { - let retryDelay = this.hls.config.levelLoadingRetryDelay; - logger.warn(`level controller,${details}, but media buffered, retry in ${retryDelay}ms`); - this.timer = setTimeout(this.ontick, retryDelay); + logger.warn(`level controller,${details}, but media buffered, retry in ${config.levelLoadingRetryDelay}ms`); + this.timer = setTimeout(this.ontick, config.levelLoadingRetryDelay); // boolean used to inform stream controller not to switch back to IDLE on non fatal error data.levelRetry = true; } else { From 2f498f1e841639a43fedb09f45d848a91a90ffab Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 16:41:28 -0400 Subject: [PATCH 07/12] convert to boolean media --- src/controller/level-controller.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 9ea64d48672..4e11d7d9dd9 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -275,7 +275,7 @@ class LevelController extends EventHandler { logger.warn(`level controller,${details} for level ${levelIndex}: switching to redundant stream id ${level.urlId}`); } else { // we could try to recover if in auto mode and current level not lowest level (0) - if (this._manualLevel === -1 && levelIndex !== 0) { + if ((this._manualLevel === -1) && levelIndex !== 0) { logger.warn(`level controller,${details}: switch-down for next fragment`); this.hls.nextAutoLevel = Math.max(0, levelIndex - 1); } else if (level && level.details && level.details.live) { @@ -287,8 +287,9 @@ class LevelController extends EventHandler { // other errors are handled by stream controller } else if (levelError === true) { // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end - let mediaBuffered = media && BufferHelper.isBuffered(media, media.currentTime) && BufferHelper.isBuffered(media, media.currentTime + 0.5); - if (mediaBuffered) { + let mediaBuffered = !!media && BufferHelper.isBuffered(media, media.currentTime) && BufferHelper.isBuffered(media, media.currentTime + 0.5); + // FIXME Rely on Level Retry parameters, now it's possible to retry as long as media is buffered + if (mediaBuffered === true) { logger.warn(`level controller,${details}, but media buffered, retry in ${config.levelLoadingRetryDelay}ms`); this.timer = setTimeout(this.ontick, config.levelLoadingRetryDelay); // boolean used to inform stream controller not to switch back to IDLE on non fatal error From 85b4d82cef261ed90dbd26c8094dc2041e3fab6d Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 16:46:44 -0400 Subject: [PATCH 08/12] init timer variable --- src/controller/level-controller.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 4e11d7d9dd9..3d1b4261cdb 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -19,6 +19,7 @@ class LevelController extends EventHandler { Event.ERROR); this.ontick = this.tick.bind(this); this._manualLevel = -1; + this.timer = null; } destroy() { @@ -27,7 +28,7 @@ class LevelController extends EventHandler { } cleanTimer() { - if (this.timer) { + if (this.timer !== null) { clearTimeout(this.timer); this.timer = null; } @@ -344,7 +345,7 @@ class LevelController extends EventHandler { logger.log(`live playlist, reload in ${reloadInterval} ms`); this.timer = setTimeout(this.ontick,reloadInterval); } else { - this.timer = null; + this.cleanTimer(); } } } From 9f0e050202a72f1381b372fa36bbc5ad1f77016c Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 16:52:01 -0400 Subject: [PATCH 09/12] change fragment loaded event handler --- src/controller/level-controller.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 3d1b4261cdb..2fc4964cda3 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -308,12 +308,12 @@ class LevelController extends EventHandler { } } - // reset level load error counter on successful frag loaded - onFragLoaded(data) { - const fragLoaded = data.frag; - if (fragLoaded && fragLoaded.type === 'main') { - const level = this._levels[fragLoaded.level]; - if (level) { + // reset errors on the successful load of a fragment + onFragLoaded({frag}) { + if (frag !== undefined && frag.type === 'main') { + const level = this._levels[frag.level]; + if (level !== undefined) { + level.fragmentError = false; level.loadError = 0; } } From 46ad24e1bd2fb8ae45be1da9e0161454548e6764 Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 17:02:18 -0400 Subject: [PATCH 10/12] prevent error level counter reset --- src/controller/level-controller.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 2fc4964cda3..3f66a7359ef 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -321,29 +321,31 @@ class LevelController extends EventHandler { onLevelLoaded(data) { const levelId = data.level; - // only process level loaded events matching with expected level + // only process level loaded events matching with expected level if (levelId === this._level) { let curLevel = this._levels[levelId]; - // reset level load error counter on successful level loaded - curLevel.loadError = 0; + // reset level load error counter on successful level loaded only if there is no issues with fragments + if(curLevel.fragmentError === false){ + curLevel.loadError = 0; + } let newDetails = data.details; // if current playlist is a live playlist, arm a timer to reload it if (newDetails.live) { - let reloadInterval = 1000*( newDetails.averagetargetduration ? newDetails.averagetargetduration : newDetails.targetduration), - curDetails = curLevel.details; + let reloadInterval = 1000 * ( newDetails.averagetargetduration ? newDetails.averagetargetduration : newDetails.targetduration), + curDetails = curLevel.details; if (curDetails && newDetails.endSN === curDetails.endSN) { // follow HLS Spec, If the client reloads a Playlist file and finds that it has not // changed then it MUST wait for a period of one-half the target // duration before retrying. - reloadInterval /=2; + reloadInterval /= 2; logger.log(`same live playlist, reload twice faster`); } // decrement reloadInterval with level loading delay reloadInterval -= performance.now() - data.stats.trequest; // in any case, don't reload more than every second - reloadInterval = Math.max(1000,Math.round(reloadInterval)); + reloadInterval = Math.max(1000, Math.round(reloadInterval)); logger.log(`live playlist, reload in ${reloadInterval} ms`); - this.timer = setTimeout(this.ontick,reloadInterval); + this.timer = setTimeout(this.ontick, reloadInterval); } else { this.cleanTimer(); } From ac632f83798003ef7769285a3447a68dc59a46b3 Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 28 Aug 2017 17:09:52 -0400 Subject: [PATCH 11/12] remove bind --- src/controller/level-controller.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 3f66a7359ef..4f144b92b87 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -17,7 +17,6 @@ class LevelController extends EventHandler { Event.LEVEL_LOADED, Event.FRAG_LOADED, Event.ERROR); - this.ontick = this.tick.bind(this); this._manualLevel = -1; this.timer = null; } @@ -292,7 +291,7 @@ class LevelController extends EventHandler { // FIXME Rely on Level Retry parameters, now it's possible to retry as long as media is buffered if (mediaBuffered === true) { logger.warn(`level controller,${details}, but media buffered, retry in ${config.levelLoadingRetryDelay}ms`); - this.timer = setTimeout(this.ontick, config.levelLoadingRetryDelay); + this.timer = setTimeout(() => this.tick(), config.levelLoadingRetryDelay); // boolean used to inform stream controller not to switch back to IDLE on non fatal error data.levelRetry = true; } else { @@ -345,7 +344,7 @@ class LevelController extends EventHandler { // in any case, don't reload more than every second reloadInterval = Math.max(1000, Math.round(reloadInterval)); logger.log(`live playlist, reload in ${reloadInterval} ms`); - this.timer = setTimeout(this.ontick, reloadInterval); + this.timer = setTimeout(() => this.tick(), reloadInterval); } else { this.cleanTimer(); } @@ -380,4 +379,3 @@ class LevelController extends EventHandler { } export default LevelController; - From 0bca151bb27bfb3ad59f5ba2369224310f03b7c3 Mon Sep 17 00:00:00 2001 From: Nicolas Siver Date: Mon, 11 Sep 2017 11:44:21 +0200 Subject: [PATCH 12/12] change codec detection to short notation --- src/controller/level-controller.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/controller/level-controller.js b/src/controller/level-controller.js index 4f144b92b87..70336e79acd 100644 --- a/src/controller/level-controller.js +++ b/src/controller/level-controller.js @@ -70,15 +70,13 @@ class LevelController extends EventHandler { level.loadError = 0; level.fragmentError = false; - if (level.videoCodec) { - videoCodecFound = true; - } + videoCodecFound = videoCodecFound || !!level.videoCodec; + audioCodecFound = audioCodecFound || !!level.audioCodec || !!(level.attrs && level.attrs.AUDIO); + // erase audio codec info if browser does not support mp4a.40.34. // demuxer will autodetect codec and fallback to mpeg/audio if (chromeOrFirefox === true && level.audioCodec && level.audioCodec.indexOf('mp4a.40.34') !== -1) { level.audioCodec = undefined; - } else if (level.audioCodec || (level.attrs && level.attrs.AUDIO)) { - audioCodecFound = true; } levelFromSet = levelSet[level.bitrate];