From a6be8eeb6bde76d253ccb8aebcd480f5ef1aa44c Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 4 Aug 2020 12:09:31 +0100 Subject: [PATCH] Prevent extractor reuse after upstream discard. After discarding upstream we shouldn't reuse the extractor from the (newly) last media chunk because the extractor may have been reused already by the discarded chunks. Also add an assertion to SampleQueue that prevents the hard-to-detect failure mode of overlapping sample byte ranges. Issue: #7690 PiperOrigin-RevId: 324785093 --- .../google/android/exoplayer2/source/SampleQueue.java | 7 +++++++ .../android/exoplayer2/source/hls/HlsMediaChunk.java | 9 ++++++++- .../exoplayer2/source/hls/HlsSampleStreamWrapper.java | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java index f2fdf835ed4..ca418965256 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java @@ -713,6 +713,13 @@ private synchronized void commitSample( long offset, int size, @Nullable CryptoData cryptoData) { + if (length > 0) { + // Ensure sample data doesn't overlap. + int previousSampleRelativeIndex = getRelativeIndex(length - 1); + checkArgument( + offsets[previousSampleRelativeIndex] + sizes[previousSampleRelativeIndex] <= offset); + } + isLastSampleQueued = (sampleFlags & C.BUFFER_FLAG_LAST_SAMPLE) != 0; largestQueuedTimestampUs = max(largestQueuedTimestampUs, timeUs); diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java index 545b60b04d8..9994ede1cf4 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java @@ -143,6 +143,7 @@ public static HlsMediaChunk createInstance( shouldSpliceIn = !canContinueWithoutSplice; previousExtractor = isFollowingChunk + && !previousChunk.extractorInvalidated && previousChunk.discontinuitySequenceNumber == discontinuitySequenceNumber ? previousChunk.extractor : null; @@ -224,6 +225,7 @@ public static HlsMediaChunk createInstance( private volatile boolean loadCanceled; private boolean loadCompleted; private ImmutableList sampleQueueFirstSampleIndices; + private boolean extractorInvalidated; private HlsMediaChunk( HlsExtractorFactory extractorFactory, @@ -300,7 +302,7 @@ public void init(HlsSampleStreamWrapper output, ImmutableList sampleQue * @param sampleQueueIndex The index of the sample queue in the output. * @return The first sample index of this chunk in the specified sample queue. */ - int getFirstSampleIndex(int sampleQueueIndex) { + public int getFirstSampleIndex(int sampleQueueIndex) { Assertions.checkState(!shouldSpliceIn); if (sampleQueueIndex >= sampleQueueFirstSampleIndices.size()) { // The sample queue was created by this chunk or a later chunk. @@ -309,6 +311,11 @@ int getFirstSampleIndex(int sampleQueueIndex) { return sampleQueueFirstSampleIndices.get(sampleQueueIndex); } + /** Prevents the extractor from being reused by a following media chunk. */ + public void invalidateExtractor() { + extractorInvalidated = true; + } + @Override public boolean isLoadCompleted() { return loadCompleted; diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index 18f9ac06371..dc3d090e82b 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -64,6 +64,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import java.io.EOFException; import java.io.IOException; import java.util.ArrayList; @@ -833,6 +834,8 @@ public LoadErrorAction onLoadError( Assertions.checkState(removed == loadable); if (mediaChunks.isEmpty()) { pendingResetPositionUs = lastSeekPositionUs; + } else { + Iterables.getLast(mediaChunks).invalidateExtractor(); } } loadErrorAction = Loader.DONT_RETRY; @@ -914,6 +917,8 @@ private void discardUpstream(int preferredQueueSize) { HlsMediaChunk firstRemovedChunk = discardUpstreamMediaChunksFromIndex(newQueueSize); if (mediaChunks.isEmpty()) { pendingResetPositionUs = lastSeekPositionUs; + } else { + Iterables.getLast(mediaChunks).invalidateExtractor(); } loadingFinished = false;