From bb25ff876b4cb48df98d8f7d033a79a9026b2cdf Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Wed, 14 Dec 2022 13:51:07 -0800 Subject: [PATCH] fix: Fix duplicate updates in StreamingEngine (#4840) There were several places where certain StreamingEngine events and timing could result in duplicate update requests for streams. This caused some inconsistent test failures, in particular on Chromecast. This change adds some additional checks before scheduling and cancelling updates, to ensure that the proper conditions are maintained. Closes #4831 --- lib/media/streaming_engine.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/media/streaming_engine.js b/lib/media/streaming_engine.js index edce4301bd..746a19c327 100644 --- a/lib/media/streaming_engine.js +++ b/lib/media/streaming_engine.js @@ -675,7 +675,7 @@ shaka.media.StreamingEngine = class { /** * Clear the buffer for a given stream. Unlike clearBuffer_, this will handle - * cases where a MediaState is performing an update. After this runs, every + * cases where a MediaState is performing an update. After this runs, the * MediaState will have a pending update. * @param {!shaka.media.StreamingEngine.MediaState_} mediaState * @private @@ -791,9 +791,9 @@ shaka.media.StreamingEngine = class { for (const type of streamsByType.keys()) { const stream = streamsByType.get(type); if (!this.mediaStates_.has(type)) { - const state = this.createMediaState_(stream); - this.mediaStates_.set(type, state); - this.scheduleUpdate_(state, 0); + const mediaState = this.createMediaState_(stream); + this.mediaStates_.set(type, mediaState); + this.scheduleUpdate_(mediaState, 0); } } } @@ -905,7 +905,7 @@ shaka.media.StreamingEngine = class { 'mediastate.stream should not have segmentIndex yet.'); thisStream.closeSegmentIndex(); } - if (mediaState.updateTimer == null) { + if (!mediaState.performingUpdate && !mediaState.updateTimer) { this.scheduleUpdate_(mediaState, 0); } return; @@ -1389,7 +1389,7 @@ shaka.media.StreamingEngine = class { // If the network slows down, abort the current fetch request and start // a new one, and ignore the error message. mediaState.performingUpdate = false; - mediaState.updateTimer = null; + this.cancelUpdate_(mediaState); this.scheduleUpdate_(mediaState, 0); } else if (mediaState.type == ContentType.TEXT && this.config_.ignoreTextStreamFailures) { @@ -1462,7 +1462,10 @@ shaka.media.StreamingEngine = class { for (const mediaState of this.mediaStates_.values()) { const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState); - if (mediaState.hasError) { + // Only schedule an update if it has an error, but it's not mid-update + // and there is not already an update scheduled. + if (mediaState.hasError && !mediaState.performingUpdate && + !mediaState.updateTimer) { shaka.log.info(logPrefix, 'Retrying after failure...'); mediaState.hasError = false; this.scheduleUpdate_(mediaState, delaySeconds); @@ -1967,7 +1970,11 @@ shaka.media.StreamingEngine = class { shaka.log.debug(logPrefix, 'cleared buffer'); mediaState.clearingBuffer = false; mediaState.endOfStream = false; - this.scheduleUpdate_(mediaState, 0); + // Since the clear operation was async, check to make sure we're not doing + // another update and we don't have one scheduled yet. + if (!mediaState.performingUpdate && !mediaState.updateTimer) { + this.scheduleUpdate_(mediaState, 0); + } }