diff --git a/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java b/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java index 1432212b86..ed0573e58a 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java @@ -160,6 +160,9 @@ public class HlsSampleSource implements SampleSource, Loader.Callback { Assertions.checkState(prepared); Assertions.checkState(enabledTrackCount > 0); downstreamPositionUs = playbackPositionUs; + if (!extractors.isEmpty()) { + discardSamplesForDisabledTracks(extractors.getFirst(), downstreamPositionUs); + } return continueBufferingInternal(); } @@ -168,7 +171,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback { if (isPendingReset() || extractors.isEmpty()) { return false; } - boolean haveSamples = extractors.getFirst().hasSamples(); + boolean haveSamples = prepared && haveSamplesForEnabledTracks(extractors.getFirst()); if (!haveSamples) { maybeThrowLoadableException(); } @@ -192,7 +195,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback { } TsExtractor extractor = extractors.getFirst(); - while (extractors.size() > 1 && !extractor.hasSamples()) { + while (extractors.size() > 1 && !haveSamplesForEnabledTracks(extractor)) { // We're finished reading from the extractor for all tracks, and so can discard it. extractors.removeFirst().release(); extractor = extractors.getFirst(); @@ -315,6 +318,23 @@ public class HlsSampleSource implements SampleSource, Loader.Callback { maybeStartLoading(); } + private void discardSamplesForDisabledTracks(TsExtractor extractor, long timeUs) { + for (int i = 0; i < trackEnabledStates.length; i++) { + if (!trackEnabledStates[i]) { + extractor.discardUntil(i, timeUs); + } + } + } + + private boolean haveSamplesForEnabledTracks(TsExtractor extractor) { + for (int i = 0; i < trackEnabledStates.length; i++) { + if (trackEnabledStates[i] && extractor.hasSamples(i)) { + return true; + } + } + return false; + } + private void maybeThrowLoadableException() throws IOException { if (currentLoadableException != null && (currentLoadableExceptionFatal || currentLoadableExceptionCount > minLoadableRetryCount)) { diff --git a/library/src/main/java/com/google/android/exoplayer/hls/TsExtractor.java b/library/src/main/java/com/google/android/exoplayer/hls/TsExtractor.java index e8a8f81fc0..f59a5206f9 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/TsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/TsExtractor.java @@ -187,19 +187,14 @@ public final class TsExtractor { } /** - * Whether samples are available for reading from {@link #getSample(int, SampleHolder)} for any - * track. + * Discards samples for the specified track up to the specified time. * - * @return True if samples are available for reading from {@link #getSample(int, SampleHolder)} - * for any track. False otherwise. + * @param track The track from which samples should be discarded. + * @param timeUs The time up to which samples should be discarded, in microseconds. */ - public boolean hasSamples() { - for (int i = 0; i < sampleQueues.size(); i++) { - if (hasSamples(i)) { - return true; - } - } - return false; + public void discardUntil(int track, long timeUs) { + Assertions.checkState(prepared); + sampleQueues.valueAt(track).discardUntil(timeUs); } /** @@ -519,7 +514,7 @@ public final class TsExtractor { private final ConcurrentLinkedQueue internalQueue; // Accessed only by the consuming thread. - private boolean readFirstFrame; + private boolean needKeyframe; private long lastReadTimeUs; private long spliceOutTimeUs; @@ -529,8 +524,9 @@ public final class TsExtractor { protected SampleQueue(SamplePool samplePool) { this.samplePool = samplePool; internalQueue = new ConcurrentLinkedQueue(); - spliceOutTimeUs = Long.MIN_VALUE; + needKeyframe = true; lastReadTimeUs = Long.MIN_VALUE; + spliceOutTimeUs = Long.MIN_VALUE; } public boolean hasMediaFormat() { @@ -557,7 +553,7 @@ public final class TsExtractor { Sample head = peek(); if (head != null) { internalQueue.remove(); - readFirstFrame = true; + needKeyframe = false; lastReadTimeUs = head.timeUs; } return head; @@ -570,7 +566,7 @@ public final class TsExtractor { */ public Sample peek() { Sample head = internalQueue.peek(); - if (!readFirstFrame) { + if (needKeyframe) { // Peeking discard of samples until we find a keyframe or run out of available samples. while (head != null && !head.isKeyframe) { recycle(head); @@ -590,6 +586,24 @@ public final class TsExtractor { return head; } + /** + * Discards samples from the queue up to the specified time. + * + * @param timeUs The time up to which samples should be discarded, in microseconds. + */ + public void discardUntil(long timeUs) { + Sample head = peek(); + while (head != null && head.timeUs < timeUs) { + recycle(head); + internalQueue.remove(); + head = internalQueue.peek(); + // We're discarding at least one sample, so any subsequent read will need to start at + // a keyframe. + needKeyframe = true; + } + lastReadTimeUs = Long.MIN_VALUE; + } + /** * Clears the queue. */