diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 1d5125384f..df822a3867 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -1006,7 +1006,7 @@ import java.util.Collections; // The track reselection didn't affect any period that has been read. selectionsChangedForReadPeriod = false; } - periodHolder = periodHolder.next; + periodHolder = periodHolder.getNext(); } if (selectionsChangedForReadPeriod) { @@ -1078,7 +1078,7 @@ import java.util.Collections; } } } - periodHolder = periodHolder.next; + periodHolder = periodHolder.getNext(); } } @@ -1107,11 +1107,12 @@ import java.util.Collections; private boolean isTimelineReady() { MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod(); + MediaPeriodHolder nextPeriodHolder = playingPeriodHolder.getNext(); long playingPeriodDurationUs = playingPeriodHolder.info.durationUs; return playingPeriodDurationUs == C.TIME_UNSET || playbackInfo.positionUs < playingPeriodDurationUs - || (playingPeriodHolder.next != null - && (playingPeriodHolder.next.prepared || playingPeriodHolder.next.info.id.isAd())); + || (nextPeriodHolder != null + && (nextPeriodHolder.prepared || nextPeriodHolder.info.id.isAd())); } private void maybeThrowSourceInfoRefreshError() throws IOException { @@ -1130,8 +1131,9 @@ import java.util.Collections; private void maybeThrowPeriodPrepareError() throws IOException { MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod(); MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod(); - if (loadingPeriodHolder != null && !loadingPeriodHolder.prepared - && (readingPeriodHolder == null || readingPeriodHolder.next == loadingPeriodHolder)) { + if (loadingPeriodHolder != null + && !loadingPeriodHolder.prepared + && (readingPeriodHolder == null || readingPeriodHolder.getNext() == loadingPeriodHolder)) { for (Renderer renderer : enabledRenderers) { if (!renderer.hasReadStreamToEnd()) { return; @@ -1241,8 +1243,8 @@ import java.util.Collections; MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(newPeriodUid, contentPositionUs); if (periodHolder != null) { // Update the new playing media period info if it already exists. - while (periodHolder.next != null) { - periodHolder = periodHolder.next; + while (periodHolder.getNext() != null) { + periodHolder = periodHolder.getNext(); if (periodHolder.info.id.equals(periodId)) { periodHolder.info = queue.getUpdatedMediaPeriodInfo(periodHolder.info); } @@ -1404,7 +1406,7 @@ import java.util.Collections; boolean advancedPlayingPeriod = false; while (playWhenReady && playingPeriodHolder != readingPeriodHolder - && rendererPositionUs >= playingPeriodHolder.next.getStartPositionRendererTime()) { + && rendererPositionUs >= playingPeriodHolder.getNext().getStartPositionRendererTime()) { // All enabled renderers' streams have been read to the end, and the playback position reached // the end of the playing period, so advance playback to the next period. if (advancedPlayingPeriod) { @@ -1440,7 +1442,7 @@ import java.util.Collections; } // Advance the reading period if necessary. - if (readingPeriodHolder.next == null) { + if (readingPeriodHolder.getNext() == null) { // We don't have a successor to advance the reading period to. return; } @@ -1455,7 +1457,7 @@ import java.util.Collections; } } - if (!readingPeriodHolder.next.prepared) { + if (!readingPeriodHolder.getNext().prepared) { // The successor is not prepared yet. maybeThrowPeriodPrepareError(); return; @@ -1655,8 +1657,8 @@ import java.util.Collections; private boolean rendererWaitingForNextStream(Renderer renderer) { MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod(); - return readingPeriodHolder.next != null && readingPeriodHolder.next.prepared - && renderer.hasReadStreamToEnd(); + MediaPeriodHolder nextPeriodHolder = readingPeriodHolder.getNext(); + return nextPeriodHolder != null && nextPeriodHolder.prepared && renderer.hasReadStreamToEnd(); } private void handleLoadingMediaPeriodChanged(boolean loadingTrackSelectionChanged) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java index a59ee61088..526ad63768 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.source.ClippingMediaPeriod; import com.google.android.exoplayer2.source.EmptySampleStream; import com.google.android.exoplayer2.source.MediaPeriod; @@ -42,7 +43,6 @@ import com.google.android.exoplayer2.util.Log; public boolean prepared; public boolean hasEnabledTracks; public MediaPeriodInfo info; - public MediaPeriodHolder next; public TrackGroupArray trackGroups; public TrackSelectorResult trackSelectorResult; @@ -50,6 +50,7 @@ import com.google.android.exoplayer2.util.Log; private final TrackSelector trackSelector; private final MediaSource mediaSource; + private MediaPeriodHolder next; private long rendererPositionOffsetUs; private TrackSelectorResult periodTrackSelectorResult; @@ -190,7 +191,9 @@ import com.google.android.exoplayer2.util.Log; // Undo the effect of previous call to associate no-sample renderers with empty tracks // so the mediaPeriod receives back whatever it sent us before. disassociateNoSampleRenderersWithEmptySampleStream(sampleStreams); - updatePeriodTrackSelectorResult(trackSelectorResult); + disableTrackSelectionsInResult(); + periodTrackSelectorResult = trackSelectorResult; + enableTrackSelectionsInResult(); // Disable streams on the period and get new streams for updated/newly-enabled tracks. TrackSelectionArray trackSelections = trackSelectorResult.selections; positionUs = @@ -219,7 +222,8 @@ import com.google.android.exoplayer2.util.Log; } public void release() { - updatePeriodTrackSelectorResult(null); + disableTrackSelectionsInResult(); + periodTrackSelectorResult = null; try { if (info.id.endPositionUs != C.TIME_END_OF_SOURCE) { mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod); @@ -232,30 +236,40 @@ import com.google.android.exoplayer2.util.Log; } } - private void updatePeriodTrackSelectorResult(TrackSelectorResult trackSelectorResult) { - if (periodTrackSelectorResult != null) { - disableTrackSelectionsInResult(periodTrackSelectorResult); - } - periodTrackSelectorResult = trackSelectorResult; - if (periodTrackSelectorResult != null) { - enableTrackSelectionsInResult(periodTrackSelectorResult); + public void setNext(@Nullable MediaPeriodHolder nextMediaPeriodHolder) { + if (nextMediaPeriodHolder == next) { + return; } + disableTrackSelectionsInResult(); + next = nextMediaPeriodHolder; + enableTrackSelectionsInResult(); } - private void enableTrackSelectionsInResult(TrackSelectorResult trackSelectorResult) { - for (int i = 0; i < trackSelectorResult.length; i++) { - boolean rendererEnabled = trackSelectorResult.isRendererEnabled(i); - TrackSelection trackSelection = trackSelectorResult.selections.get(i); + @Nullable + public MediaPeriodHolder getNext() { + return next; + } + + private void enableTrackSelectionsInResult() { + if (!isLoadingMediaPeriod() || periodTrackSelectorResult == null) { + return; + } + for (int i = 0; i < periodTrackSelectorResult.length; i++) { + boolean rendererEnabled = periodTrackSelectorResult.isRendererEnabled(i); + TrackSelection trackSelection = periodTrackSelectorResult.selections.get(i); if (rendererEnabled && trackSelection != null) { trackSelection.enable(); } } } - private void disableTrackSelectionsInResult(TrackSelectorResult trackSelectorResult) { - for (int i = 0; i < trackSelectorResult.length; i++) { - boolean rendererEnabled = trackSelectorResult.isRendererEnabled(i); - TrackSelection trackSelection = trackSelectorResult.selections.get(i); + private void disableTrackSelectionsInResult() { + if (!isLoadingMediaPeriod() || periodTrackSelectorResult == null) { + return; + } + for (int i = 0; i < periodTrackSelectorResult.length; i++) { + boolean rendererEnabled = periodTrackSelectorResult.isRendererEnabled(i); + TrackSelection trackSelection = periodTrackSelectorResult.selections.get(i); if (rendererEnabled && trackSelection != null) { trackSelection.disable(); } @@ -286,4 +300,8 @@ import com.google.android.exoplayer2.util.Log; } } } + + private boolean isLoadingMediaPeriod() { + return next == null; + } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java index 2edf7bb8c6..6370299334 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java @@ -156,7 +156,7 @@ import com.google.android.exoplayer2.util.Assertions; info); if (loading != null) { Assertions.checkState(hasPlayingPeriod()); - loading.next = newPeriodHolder; + loading.setNext(newPeriodHolder); } oldFrontPeriodUid = null; loading = newPeriodHolder; @@ -207,8 +207,8 @@ import com.google.android.exoplayer2.util.Assertions; * @return The updated reading period holder. */ public MediaPeriodHolder advanceReadingPeriod() { - Assertions.checkState(reading != null && reading.next != null); - reading = reading.next; + Assertions.checkState(reading != null && reading.getNext() != null); + reading = reading.getNext(); return reading; } @@ -222,7 +222,7 @@ import com.google.android.exoplayer2.util.Assertions; public MediaPeriodHolder advancePlayingPeriod() { if (playing != null) { if (playing == reading) { - reading = playing.next; + reading = playing.getNext(); } playing.release(); length--; @@ -231,7 +231,7 @@ import com.google.android.exoplayer2.util.Assertions; oldFrontPeriodUid = playing.uid; oldFrontPeriodWindowSequenceNumber = playing.info.id.windowSequenceNumber; } - playing = playing.next; + playing = playing.getNext(); } else { playing = loading; reading = loading; @@ -251,8 +251,8 @@ import com.google.android.exoplayer2.util.Assertions; Assertions.checkState(mediaPeriodHolder != null); boolean removedReading = false; loading = mediaPeriodHolder; - while (mediaPeriodHolder.next != null) { - mediaPeriodHolder = mediaPeriodHolder.next; + while (mediaPeriodHolder.getNext() != null) { + mediaPeriodHolder = mediaPeriodHolder.getNext(); if (mediaPeriodHolder == reading) { reading = playing; removedReading = true; @@ -260,7 +260,7 @@ import com.google.android.exoplayer2.util.Assertions; mediaPeriodHolder.release(); length--; } - loading.next = null; + loading.setNext(null); return removedReading; } @@ -337,7 +337,7 @@ import com.google.android.exoplayer2.util.Assertions; } previousPeriodHolder = periodHolder; - periodHolder = periodHolder.next; + periodHolder = periodHolder.getNext(); } return true; } @@ -439,7 +439,7 @@ import com.google.android.exoplayer2.util.Assertions; // Reuse window sequence number of first exact period match. return mediaPeriodHolder.info.id.windowSequenceNumber; } - mediaPeriodHolder = mediaPeriodHolder.next; + mediaPeriodHolder = mediaPeriodHolder.getNext(); } mediaPeriodHolder = getFrontPeriod(); while (mediaPeriodHolder != null) { @@ -451,7 +451,7 @@ import com.google.android.exoplayer2.util.Assertions; return mediaPeriodHolder.info.id.windowSequenceNumber; } } - mediaPeriodHolder = mediaPeriodHolder.next; + mediaPeriodHolder = mediaPeriodHolder.getNext(); } // If no match is found, create new sequence number. return nextWindowSequenceNumber++; @@ -482,19 +482,20 @@ import com.google.android.exoplayer2.util.Assertions; int nextPeriodIndex = timeline.getNextPeriodIndex( currentPeriodIndex, period, window, repeatMode, shuffleModeEnabled); - while (lastValidPeriodHolder.next != null + while (lastValidPeriodHolder.getNext() != null && !lastValidPeriodHolder.info.isLastInTimelinePeriod) { - lastValidPeriodHolder = lastValidPeriodHolder.next; + lastValidPeriodHolder = lastValidPeriodHolder.getNext(); } - if (nextPeriodIndex == C.INDEX_UNSET || lastValidPeriodHolder.next == null) { + MediaPeriodHolder nextMediaPeriodHolder = lastValidPeriodHolder.getNext(); + if (nextPeriodIndex == C.INDEX_UNSET || nextMediaPeriodHolder == null) { break; } - int nextPeriodHolderPeriodIndex = timeline.getIndexOfPeriod(lastValidPeriodHolder.next.uid); + int nextPeriodHolderPeriodIndex = timeline.getIndexOfPeriod(nextMediaPeriodHolder.uid); if (nextPeriodHolderPeriodIndex != nextPeriodIndex) { break; } - lastValidPeriodHolder = lastValidPeriodHolder.next; + lastValidPeriodHolder = nextMediaPeriodHolder; currentPeriodIndex = nextPeriodIndex; } @@ -567,8 +568,9 @@ import com.google.android.exoplayer2.util.Assertions; } nextPeriodUid = defaultPosition.first; startPositionUs = defaultPosition.second; - if (mediaPeriodHolder.next != null && mediaPeriodHolder.next.uid.equals(nextPeriodUid)) { - windowSequenceNumber = mediaPeriodHolder.next.info.id.windowSequenceNumber; + MediaPeriodHolder nextMediaPeriodHolder = mediaPeriodHolder.getNext(); + if (nextMediaPeriodHolder != null && nextMediaPeriodHolder.uid.equals(nextPeriodUid)) { + windowSequenceNumber = nextMediaPeriodHolder.info.id.windowSequenceNumber; } else { windowSequenceNumber = nextWindowSequenceNumber++; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java index 78d052ac3c..a7c0658708 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java @@ -29,8 +29,9 @@ import java.util.List; * TrackGroup}, and a possibly varying individual selected track from the subset. * *

Tracks belonging to the subset are exposed in decreasing bandwidth order. The individual - * selected track may change as a result of calling {@link #updateSelectedTrack(long, long, long, - * List, MediaChunkIterator[])}. + * selected track may change dynamically as a result of calling {@link #updateSelectedTrack(long, + * long, long, List, MediaChunkIterator[])} or {@link #evaluateQueueSize(long, List)}. This only + * happens between calls to {@link #enable()} and {@link #disable()}. */ public interface TrackSelection { @@ -53,16 +54,20 @@ public interface TrackSelection { } /** - * Enables the track selection. - *

- * This method may not be called when the track selection is already enabled. + * Enables the track selection. Dynamic changes via {@link #updateSelectedTrack(long, long, long, + * List, MediaChunkIterator[])} or {@link #evaluateQueueSize(long, List)} will only happen after + * this call. + * + *

This method may not be called when the track selection is already enabled. */ void enable(); /** - * Disables this track selection. - *

- * This method may only be called when the track selection is already enabled. + * Disables this track selection. No further dynamic changes via {@link #updateSelectedTrack(long, + * long, long, List, MediaChunkIterator[])} or {@link #evaluateQueueSize(long, List)} will happen + * after this call. + * + *

This method may only be called when the track selection is already enabled. */ void disable();