diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java index 9d7eb774e6..c5c506d2fa 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java @@ -242,6 +242,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return trackGroup; } + /** Returns if the chunk source has independent segments. */ + public boolean hasIndependentSegments() { + return independentSegments; + } + /** * Sets the current track selection. * diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java index 005ccafda5..9fd47fa5b6 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java @@ -496,8 +496,21 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; return true; } + // Detect whether the seek is to the start of a chunk that's at least partially buffered. + @Nullable HlsMediaChunk seekToMediaChunk = null; + if (chunkSource.hasIndependentSegments()) { + for (int i = 0; i < mediaChunks.size(); i++) { + HlsMediaChunk mediaChunk = mediaChunks.get(i); + long mediaChunkStartTimeUs = mediaChunk.startTimeUs; + if (mediaChunkStartTimeUs == positionUs) { + seekToMediaChunk = mediaChunk; + break; + } + } + } + // If we're not forced to reset, try and seek within the buffer. - if (sampleQueuesBuilt && !forceReset && seekInsideBufferUs(positionUs)) { + if (sampleQueuesBuilt && !forceReset && seekInsideBufferUs(positionUs, seekToMediaChunk)) { return false; } @@ -1470,13 +1483,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * Attempts to seek to the specified position within the sample queues. * * @param positionUs The seek position in microseconds. + * @param chunk Optionally the chunk to seek to. * @return Whether the in-buffer seek was successful. */ - private boolean seekInsideBufferUs(long positionUs) { + private boolean seekInsideBufferUs(long positionUs, @Nullable HlsMediaChunk chunk) { int sampleQueueCount = sampleQueues.length; for (int i = 0; i < sampleQueueCount; i++) { SampleQueue sampleQueue = sampleQueues[i]; - boolean seekInsideQueue = sampleQueue.seekTo(positionUs, /* allowTimeBeyondBuffer= */ false); + boolean seekInsideQueue; + if (chunk != null) { + seekInsideQueue = sampleQueue.seekTo(chunk.getFirstSampleIndex(i)); + } else { + seekInsideQueue = sampleQueue.seekTo(positionUs, /* allowTimeBeyondBuffer= */ false); + } // If we have AV tracks then an in-queue seek is successful if the seek into every AV queue // is successful. We ignore whether seeks within non-AV queues are successful in this case, as // they may be sparse or poorly interleaved. If we only have non-AV tracks then a seek is