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 f2fdf835ed..ca41896525 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 @@ public class SampleQueue implements TrackOutput { 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 545b60b04d..9994ede1cf 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 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; shouldSpliceIn = !canContinueWithoutSplice; previousExtractor = isFollowingChunk + && !previousChunk.extractorInvalidated && previousChunk.discontinuitySequenceNumber == discontinuitySequenceNumber ? previousChunk.extractor : null; @@ -224,6 +225,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private volatile boolean loadCanceled; private boolean loadCompleted; private ImmutableList sampleQueueFirstSampleIndices; + private boolean extractorInvalidated; private HlsMediaChunk( HlsExtractorFactory extractorFactory, @@ -300,7 +302,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * @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 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; 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 18f9ac0637..dc3d090e82 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.MimeTypes; 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 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; Assertions.checkState(removed == loadable); if (mediaChunks.isEmpty()) { pendingResetPositionUs = lastSeekPositionUs; + } else { + Iterables.getLast(mediaChunks).invalidateExtractor(); } } loadErrorAction = Loader.DONT_RETRY; @@ -914,6 +917,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; HlsMediaChunk firstRemovedChunk = discardUpstreamMediaChunksFromIndex(newQueueSize); if (mediaChunks.isEmpty()) { pendingResetPositionUs = lastSeekPositionUs; + } else { + Iterables.getLast(mediaChunks).invalidateExtractor(); } loadingFinished = false;