diff --git a/lib/media/streaming_engine.js b/lib/media/streaming_engine.js index 3bee682a6a..fbb94d872f 100644 --- a/lib/media/streaming_engine.js +++ b/lib/media/streaming_engine.js @@ -1240,7 +1240,16 @@ shaka.media.StreamingEngine = class { if (this.config_.lowLatencyMode && isReadableStreamSupported && isMP4 && !reference.hlsAes128Key) { let remaining = new Uint8Array(0); + let processingResult = false; + let callbackCalled = false; const streamDataCallback = async (data) => { + if (processingResult) { + // If the fallback result processing was triggered, don't also + // append the buffer here. In theory this should never happen, + // but it does on some older TVs. + return; + } + callbackCalled = true; this.destroyer_.ensureNotDestroyed(); if (this.fatalError_) { return; @@ -1271,7 +1280,33 @@ shaka.media.StreamingEngine = class { } }; - await this.fetch_(mediaState, reference, streamDataCallback); + const result = + await this.fetch_(mediaState, reference, streamDataCallback); + if (!callbackCalled) { + // In some environments, we might be forced to use network plugins + // that don't support streamDataCallback. In those cases, as a + // fallback, append the buffer here. + processingResult = true; + this.destroyer_.ensureNotDestroyed(); + if (this.fatalError_) { + return; + } + + // If the text stream gets switched between fetch_() and append_(), + // the new text parser is initialized, but the new init segment is + // not fetched yet. That would cause an error in + // TextParser.parseMedia(). + // See http://b/168253400 + if (mediaState.waitingToClearBuffer) { + shaka.log.info(logPrefix, 'waitingToClearBuffer, skip append'); + mediaState.performingUpdate = false; + this.scheduleUpdate_(mediaState, 0); + return; + } + + await this.append_( + mediaState, presentationTime, stream, reference, result); + } } else { if (this.config_.lowLatencyMode && !isReadableStreamSupported) { shaka.log.warning('Low latency streaming mode is enabled, but ' +