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 df822a3867..6810c86558 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 @@ -993,12 +993,14 @@ import java.util.Collections; MediaPeriodHolder periodHolder = queue.getPlayingPeriod(); MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod(); boolean selectionsChangedForReadPeriod = true; + TrackSelectorResult newTrackSelectorResult = null; while (true) { if (periodHolder == null || !periodHolder.prepared) { // The reselection did not change any prepared periods. return; } - if (periodHolder.selectTracks(playbackSpeed, playbackInfo.timeline)) { + newTrackSelectorResult = periodHolder.selectTracks(playbackSpeed, playbackInfo.timeline); + if (newTrackSelectorResult != null) { // Selected tracks have changed for this period. break; } @@ -1017,7 +1019,7 @@ import java.util.Collections; boolean[] streamResetFlags = new boolean[renderers.length]; long periodPositionUs = playingPeriodHolder.applyTrackSelection( - playbackInfo.positionUs, recreateStreams, streamResetFlags); + newTrackSelectorResult, playbackInfo.positionUs, recreateStreams, streamResetFlags); if (playbackInfo.playbackState != Player.STATE_ENDED && periodPositionUs != playbackInfo.positionUs) { playbackInfo = playbackInfo.fromNewPosition(playbackInfo.periodId, periodPositionUs, @@ -1047,7 +1049,7 @@ import java.util.Collections; } playbackInfo = playbackInfo.copyWithTrackInfo( - playingPeriodHolder.trackGroups, playingPeriodHolder.trackSelectorResult); + playingPeriodHolder.getTrackGroups(), playingPeriodHolder.getTrackSelectorResult()); enableRenderers(rendererWasEnabledFlags, enabledRendererCount); } else { // Release and re-prepare/buffer periods after the one whose selection changed. @@ -1056,7 +1058,7 @@ import java.util.Collections; long loadingPeriodPositionUs = Math.max( periodHolder.info.startPositionUs, periodHolder.toPeriodTime(rendererPositionUs)); - periodHolder.applyTrackSelection(loadingPeriodPositionUs, false); + periodHolder.applyTrackSelection(newTrackSelectorResult, loadingPeriodPositionUs, false); } } handleLoadingMediaPeriodChanged(/* loadingTrackSelectionChanged= */ true); @@ -1069,13 +1071,11 @@ import java.util.Collections; private void updateTrackSelectionPlaybackSpeed(float playbackSpeed) { MediaPeriodHolder periodHolder = queue.getFrontPeriod(); - while (periodHolder != null) { - if (periodHolder.trackSelectorResult != null) { - TrackSelection[] trackSelections = periodHolder.trackSelectorResult.selections.getAll(); - for (TrackSelection trackSelection : trackSelections) { - if (trackSelection != null) { - trackSelection.onPlaybackSpeed(playbackSpeed); - } + while (periodHolder != null && periodHolder.prepared) { + TrackSelection[] trackSelections = periodHolder.getTrackSelectorResult().selections.getAll(); + for (TrackSelection trackSelection : trackSelections) { + if (trackSelection != null) { + trackSelection.onPlaybackSpeed(playbackSpeed); } } periodHolder = periodHolder.getNext(); @@ -1463,9 +1463,9 @@ import java.util.Collections; return; } - TrackSelectorResult oldTrackSelectorResult = readingPeriodHolder.trackSelectorResult; + TrackSelectorResult oldTrackSelectorResult = readingPeriodHolder.getTrackSelectorResult(); readingPeriodHolder = queue.advanceReadingPeriod(); - TrackSelectorResult newTrackSelectorResult = readingPeriodHolder.trackSelectorResult; + TrackSelectorResult newTrackSelectorResult = readingPeriodHolder.getTrackSelectorResult(); boolean initialDiscontinuity = readingPeriodHolder.mediaPeriod.readDiscontinuity() != C.TIME_UNSET; @@ -1536,7 +1536,7 @@ import java.util.Collections; loadingPeriodHolder.handlePrepared( mediaClock.getPlaybackParameters().speed, playbackInfo.timeline); updateLoadControlTrackSelection( - loadingPeriodHolder.trackGroups, loadingPeriodHolder.trackSelectorResult); + loadingPeriodHolder.getTrackGroups(), loadingPeriodHolder.getTrackSelectorResult()); if (!queue.hasPlayingPeriod()) { // This is the first prepared period, so start playing it. MediaPeriodHolder playingPeriodHolder = queue.advancePlayingPeriod(); @@ -1596,11 +1596,11 @@ import java.util.Collections; for (int i = 0; i < renderers.length; i++) { Renderer renderer = renderers[i]; rendererWasEnabledFlags[i] = renderer.getState() != Renderer.STATE_DISABLED; - if (newPlayingPeriodHolder.trackSelectorResult.isRendererEnabled(i)) { + if (newPlayingPeriodHolder.getTrackSelectorResult().isRendererEnabled(i)) { enabledRendererCount++; } if (rendererWasEnabledFlags[i] - && (!newPlayingPeriodHolder.trackSelectorResult.isRendererEnabled(i) + && (!newPlayingPeriodHolder.getTrackSelectorResult().isRendererEnabled(i) || (renderer.isCurrentStreamFinal() && renderer.getStream() == oldPlayingPeriodHolder.sampleStreams[i]))) { // The renderer should be disabled before playing the next period, either because it's not @@ -1611,7 +1611,8 @@ import java.util.Collections; } playbackInfo = playbackInfo.copyWithTrackInfo( - newPlayingPeriodHolder.trackGroups, newPlayingPeriodHolder.trackSelectorResult); + newPlayingPeriodHolder.getTrackGroups(), + newPlayingPeriodHolder.getTrackSelectorResult()); enableRenderers(rendererWasEnabledFlags, enabledRendererCount); } @@ -1621,7 +1622,7 @@ import java.util.Collections; int enabledRendererCount = 0; MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod(); for (int i = 0; i < renderers.length; i++) { - if (playingPeriodHolder.trackSelectorResult.isRendererEnabled(i)) { + if (playingPeriodHolder.getTrackSelectorResult().isRendererEnabled(i)) { enableRenderer(i, rendererWasEnabledFlags[i], enabledRendererCount++); } } @@ -1634,10 +1635,10 @@ import java.util.Collections; Renderer renderer = renderers[rendererIndex]; enabledRenderers[enabledRendererIndex] = renderer; if (renderer.getState() == Renderer.STATE_DISABLED) { + TrackSelectorResult trackSelectorResult = playingPeriodHolder.getTrackSelectorResult(); RendererConfiguration rendererConfiguration = - playingPeriodHolder.trackSelectorResult.rendererConfigurations[rendererIndex]; - TrackSelection newSelection = playingPeriodHolder.trackSelectorResult.selections.get( - rendererIndex); + trackSelectorResult.rendererConfigurations[rendererIndex]; + TrackSelection newSelection = trackSelectorResult.selections.get(rendererIndex); Format[] formats = getFormats(newSelection); // The renderer needs enabling with its new track selection. boolean playing = playWhenReady && playbackInfo.playbackState == Player.STATE_READY; @@ -1674,7 +1675,8 @@ import java.util.Collections; && loadingMediaPeriodHolder != null && loadingMediaPeriodHolder.prepared) { updateLoadControlTrackSelection( - loadingMediaPeriodHolder.trackGroups, loadingMediaPeriodHolder.trackSelectorResult); + loadingMediaPeriodHolder.getTrackGroups(), + loadingMediaPeriodHolder.getTrackSelectorResult()); } } 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 526ad63768..3ba96d2b80 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 @@ -29,30 +29,38 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Log; +import org.checkerframework.checker.nullness.compatqual.NullableType; /** Holds a {@link MediaPeriod} with information required to play it as part of a timeline. */ /* package */ final class MediaPeriodHolder { private static final String TAG = "MediaPeriodHolder"; + /** The {@link MediaPeriod} wrapped by this class. */ public final MediaPeriod mediaPeriod; + /** The unique timeline period identifier the media period belongs to. */ public final Object uid; - public final SampleStream[] sampleStreams; - public final boolean[] mayRetainStreamFlags; + /** + * The sample streams for each renderer associated with this period. May contain null elements. + */ + public final @NullableType SampleStream[] sampleStreams; + /** Whether the media period has finished preparing. */ public boolean prepared; + /** Whether any of the tracks of this media period are enabled. */ public boolean hasEnabledTracks; + /** {@link MediaPeriodInfo} about this media period. */ public MediaPeriodInfo info; - public TrackGroupArray trackGroups; - public TrackSelectorResult trackSelectorResult; + private final boolean[] mayRetainStreamFlags; private final RendererCapabilities[] rendererCapabilities; private final TrackSelector trackSelector; private final MediaSource mediaSource; - private MediaPeriodHolder next; + @Nullable private MediaPeriodHolder next; + @Nullable private TrackGroupArray trackGroups; + @Nullable private TrackSelectorResult trackSelectorResult; private long rendererPositionOffsetUs; - private TrackSelectorResult periodTrackSelectorResult; /** * Creates a new holder with information required to play it as part of a timeline. @@ -76,7 +84,7 @@ import com.google.android.exoplayer2.util.Log; this.rendererPositionOffsetUs = rendererPositionOffsetUs - info.startPositionUs; this.trackSelector = trackSelector; this.mediaSource = mediaSource; - this.uid = Assertions.checkNotNull(info.id.periodUid); + this.uid = info.id.periodUid; this.info = info; sampleStreams = new SampleStream[rendererCapabilities.length]; mayRetainStreamFlags = new boolean[rendererCapabilities.length]; @@ -92,31 +100,38 @@ import com.google.android.exoplayer2.util.Log; this.mediaPeriod = mediaPeriod; } + /** + * Converts time relative to the start of the period to the respective renderer time using {@link + * #getRendererOffset()}, in microseconds. + */ public long toRendererTime(long periodTimeUs) { return periodTimeUs + getRendererOffset(); } + /** + * Converts renderer time to the respective time relative to the start of the period using {@link + * #getRendererOffset()}, in microseconds. + */ public long toPeriodTime(long rendererTimeUs) { return rendererTimeUs - getRendererOffset(); } + /** Returns the renderer time of the start of the period, in microseconds. */ public long getRendererOffset() { return rendererPositionOffsetUs; } + /** Returns start position of period in renderer time. */ public long getStartPositionRendererTime() { return info.startPositionUs + rendererPositionOffsetUs; } + /** Returns whether the period is fully buffered. */ public boolean isFullyBuffered() { return prepared && (!hasEnabledTracks || mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE); } - public long getDurationUs() { - return info.durationUs; - } - /** * Returns the buffered position in microseconds. If the period is buffered to the end then * {@link C#TIME_END_OF_SOURCE} is returned unless {@code convertEosToDuration} is true, in which @@ -137,65 +152,132 @@ import com.google.android.exoplayer2.util.Log; : bufferedPositionUs; } + /** + * Returns the next load time relative to the start of the period, or {@link C#TIME_END_OF_SOURCE} + * if loading has finished. + */ public long getNextLoadPositionUs() { return !prepared ? 0 : mediaPeriod.getNextLoadPositionUs(); } + /** + * Handles period preparation. + * + * @param playbackSpeed The current playback speed. + * @param timeline The current {@link Timeline}. + * @throws ExoPlaybackException If an error occurs during track selection. + */ public void handlePrepared(float playbackSpeed, Timeline timeline) throws ExoPlaybackException { prepared = true; trackGroups = mediaPeriod.getTrackGroups(); - selectTracks(playbackSpeed, timeline); - long newStartPositionUs = applyTrackSelection(info.startPositionUs, false); + TrackSelectorResult selectorResult = + Assertions.checkNotNull(selectTracks(playbackSpeed, timeline)); + long newStartPositionUs = + applyTrackSelection( + selectorResult, info.startPositionUs, /* forceRecreateStreams= */ false); rendererPositionOffsetUs += info.startPositionUs - newStartPositionUs; info = info.copyWithStartPositionUs(newStartPositionUs); } + /** + * Reevaluates the buffer of the media period at the given renderer position. Should only be + * called if this is the loading media period. + * + * @param rendererPositionUs The playing position in renderer time, in microseconds. + */ public void reevaluateBuffer(long rendererPositionUs) { + Assertions.checkState(isLoadingMediaPeriod()); if (prepared) { mediaPeriod.reevaluateBuffer(toPeriodTime(rendererPositionUs)); } } + /** + * Continues loading the media period at the given renderer position. Should only be called if + * this is the loading media period. + * + * @param rendererPositionUs The load position in renderer time, in microseconds. + */ public void continueLoading(long rendererPositionUs) { + Assertions.checkState(isLoadingMediaPeriod()); long loadingPeriodPositionUs = toPeriodTime(rendererPositionUs); mediaPeriod.continueLoading(loadingPeriodPositionUs); } - public boolean selectTracks(float playbackSpeed, Timeline timeline) throws ExoPlaybackException { + /** + * Selects tracks for the period and returns the new result if the selection changed. Must only be + * called if {@link #prepared} is {@code true}. + * + * @param playbackSpeed The current playback speed. + * @param timeline The current {@link Timeline}. + * @return The {@link TrackSelectorResult} if the result changed. Or null if nothing changed. + * @throws ExoPlaybackException If an error occurs during track selection. + */ + @Nullable + public TrackSelectorResult selectTracks(float playbackSpeed, Timeline timeline) + throws ExoPlaybackException { TrackSelectorResult selectorResult = - trackSelector.selectTracks(rendererCapabilities, trackGroups, info.id, timeline); - if (selectorResult.isEquivalent(periodTrackSelectorResult)) { - return false; + trackSelector.selectTracks(rendererCapabilities, getTrackGroups(), info.id, timeline); + if (selectorResult.isEquivalent(trackSelectorResult)) { + return null; } - trackSelectorResult = selectorResult; - for (TrackSelection trackSelection : trackSelectorResult.selections.getAll()) { + for (TrackSelection trackSelection : selectorResult.selections.getAll()) { if (trackSelection != null) { trackSelection.onPlaybackSpeed(playbackSpeed); } } - return true; - } - - public long applyTrackSelection(long positionUs, boolean forceRecreateStreams) { - return applyTrackSelection( - positionUs, forceRecreateStreams, new boolean[rendererCapabilities.length]); + return selectorResult; } + /** + * Applies a {@link TrackSelectorResult} to the period. + * + * @param trackSelectorResult The {@link TrackSelectorResult} to apply. + * @param positionUs The position relative to the start of the period at which to apply the new + * track selections, in microseconds. + * @param forceRecreateStreams Whether all streams are forced to be recreated. + * @return The actual position relative to the start of the period at which the new track + * selections are applied. + */ public long applyTrackSelection( - long positionUs, boolean forceRecreateStreams, boolean[] streamResetFlags) { - for (int i = 0; i < trackSelectorResult.length; i++) { + TrackSelectorResult trackSelectorResult, long positionUs, boolean forceRecreateStreams) { + return applyTrackSelection( + trackSelectorResult, + positionUs, + forceRecreateStreams, + new boolean[rendererCapabilities.length]); + } + + /** + * Applies a {@link TrackSelectorResult} to the period. + * + * @param newTrackSelectorResult The {@link TrackSelectorResult} to apply. + * @param positionUs The position relative to the start of the period at which to apply the new + * track selections, in microseconds. + * @param forceRecreateStreams Whether all streams are forced to be recreated. + * @param streamResetFlags Will be populated to indicate which streams have been reset or were + * newly created. + * @return The actual position relative to the start of the period at which the new track + * selections are applied. + */ + public long applyTrackSelection( + TrackSelectorResult newTrackSelectorResult, + long positionUs, + boolean forceRecreateStreams, + boolean[] streamResetFlags) { + for (int i = 0; i < newTrackSelectorResult.length; i++) { mayRetainStreamFlags[i] = - !forceRecreateStreams && trackSelectorResult.isEquivalent(periodTrackSelectorResult, i); + !forceRecreateStreams && newTrackSelectorResult.isEquivalent(trackSelectorResult, i); } // 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); disableTrackSelectionsInResult(); - periodTrackSelectorResult = trackSelectorResult; + trackSelectorResult = newTrackSelectorResult; enableTrackSelectionsInResult(); // Disable streams on the period and get new streams for updated/newly-enabled tracks. - TrackSelectionArray trackSelections = trackSelectorResult.selections; + TrackSelectionArray trackSelections = newTrackSelectorResult.selections; positionUs = mediaPeriod.selectTracks( trackSelections.getAll(), @@ -209,7 +291,7 @@ import com.google.android.exoplayer2.util.Log; hasEnabledTracks = false; for (int i = 0; i < sampleStreams.length; i++) { if (sampleStreams[i] != null) { - Assertions.checkState(trackSelectorResult.isRendererEnabled(i)); + Assertions.checkState(newTrackSelectorResult.isRendererEnabled(i)); // hasEnabledTracks should be true only when non-empty streams exists. if (rendererCapabilities[i].getTrackType() != C.TRACK_TYPE_NONE) { hasEnabledTracks = true; @@ -221,9 +303,10 @@ import com.google.android.exoplayer2.util.Log; return positionUs; } + /** Releases the media period. No other method should be called after the release. */ public void release() { disableTrackSelectionsInResult(); - periodTrackSelectorResult = null; + trackSelectorResult = null; try { if (info.id.endPositionUs != C.TIME_END_OF_SOURCE) { mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod); @@ -236,6 +319,12 @@ import com.google.android.exoplayer2.util.Log; } } + /** + * Sets the next media period holder in the queue. + * + * @param nextMediaPeriodHolder The next holder, or null if this will be the new loading media + * period holder at the end of the queue. + */ public void setNext(@Nullable MediaPeriodHolder nextMediaPeriodHolder) { if (nextMediaPeriodHolder == next) { return; @@ -245,18 +334,39 @@ import com.google.android.exoplayer2.util.Log; enableTrackSelectionsInResult(); } + /** + * Returns the next media period holder in the queue, or null if this is the last media period + * (and thus the loading media period). + */ @Nullable public MediaPeriodHolder getNext() { return next; } + /** + * Returns the {@link TrackGroupArray} exposed by this media period. Must only be called if {@link + * #prepared} is {@code true}. + */ + public TrackGroupArray getTrackGroups() { + return Assertions.checkNotNull(trackGroups); + } + + /** + * Returns the {@link TrackSelectorResult} which is currently applied. Must only be called if + * {@link #prepared} is {@code true}. + */ + public TrackSelectorResult getTrackSelectorResult() { + return Assertions.checkNotNull(trackSelectorResult); + } + private void enableTrackSelectionsInResult() { - if (!isLoadingMediaPeriod() || periodTrackSelectorResult == null) { + TrackSelectorResult trackSelectorResult = this.trackSelectorResult; + if (!isLoadingMediaPeriod() || trackSelectorResult == null) { return; } - for (int i = 0; i < periodTrackSelectorResult.length; i++) { - boolean rendererEnabled = periodTrackSelectorResult.isRendererEnabled(i); - TrackSelection trackSelection = periodTrackSelectorResult.selections.get(i); + for (int i = 0; i < trackSelectorResult.length; i++) { + boolean rendererEnabled = trackSelectorResult.isRendererEnabled(i); + TrackSelection trackSelection = trackSelectorResult.selections.get(i); if (rendererEnabled && trackSelection != null) { trackSelection.enable(); } @@ -264,12 +374,13 @@ import com.google.android.exoplayer2.util.Log; } private void disableTrackSelectionsInResult() { - if (!isLoadingMediaPeriod() || periodTrackSelectorResult == null) { + TrackSelectorResult trackSelectorResult = this.trackSelectorResult; + if (!isLoadingMediaPeriod() || trackSelectorResult == null) { return; } - for (int i = 0; i < periodTrackSelectorResult.length; i++) { - boolean rendererEnabled = periodTrackSelectorResult.isRendererEnabled(i); - TrackSelection trackSelection = periodTrackSelectorResult.selections.get(i); + for (int i = 0; i < trackSelectorResult.length; i++) { + boolean rendererEnabled = trackSelectorResult.isRendererEnabled(i); + TrackSelection trackSelection = trackSelectorResult.selections.get(i); if (rendererEnabled && trackSelection != null) { trackSelection.disable(); } @@ -280,7 +391,8 @@ import com.google.android.exoplayer2.util.Log; * For each renderer of type {@link C#TRACK_TYPE_NONE}, we will remove the dummy {@link * EmptySampleStream} that was associated with it. */ - private void disassociateNoSampleRenderersWithEmptySampleStream(SampleStream[] sampleStreams) { + private void disassociateNoSampleRenderersWithEmptySampleStream( + @NullableType SampleStream[] sampleStreams) { for (int i = 0; i < rendererCapabilities.length; i++) { if (rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_NONE) { sampleStreams[i] = null; @@ -292,7 +404,9 @@ import com.google.android.exoplayer2.util.Log; * For each renderer of type {@link C#TRACK_TYPE_NONE} that was enabled, we will associate it with * a dummy {@link EmptySampleStream}. */ - private void associateNoSampleRenderersWithEmptySampleStream(SampleStream[] sampleStreams) { + private void associateNoSampleRenderersWithEmptySampleStream( + @NullableType SampleStream[] sampleStreams) { + TrackSelectorResult trackSelectorResult = Assertions.checkNotNull(this.trackSelectorResult); for (int i = 0; i < rendererCapabilities.length; i++) { if (rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_NONE && trackSelectorResult.isRendererEnabled(i)) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java index 31daf65d38..49209023e6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java @@ -215,7 +215,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Override public long selectTracks( - TrackSelection[] selections, + @NullableType TrackSelection[] selections, boolean[] mayRetainStreamFlags, @NullableType SampleStream[] streams, boolean[] streamResetFlags, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java index 997f94bbfe..b7f5a07b60 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java @@ -21,6 +21,7 @@ import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.trackselection.TrackSelection; import java.io.IOException; +import org.checkerframework.checker.nullness.compatqual.NullableType; /** * Loads media corresponding to a {@link Timeline.Period}, and allows that media to be read. All @@ -108,9 +109,9 @@ public interface MediaPeriod extends SequenceableLoader { * @return The actual position at which the tracks were enabled, in microseconds. */ long selectTracks( - TrackSelection[] selections, + @NullableType TrackSelection[] selections, boolean[] mayRetainStreamFlags, - SampleStream[] streams, + @NullableType SampleStream[] streams, boolean[] streamResetFlags, long positionUs); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectorResult.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectorResult.java index f1136f0be5..4bc4975a1c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectorResult.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectorResult.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.trackselection; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.RendererConfiguration; import com.google.android.exoplayer2.util.Util; import org.checkerframework.checker.nullness.compatqual.NullableType; @@ -70,7 +71,7 @@ public final class TrackSelectorResult { * will be returned. * @return Whether this result is equivalent to {@code other} for all renderers. */ - public boolean isEquivalent(TrackSelectorResult other) { + public boolean isEquivalent(@Nullable TrackSelectorResult other) { if (other == null || other.selections.length != selections.length) { return false; } @@ -93,7 +94,7 @@ public final class TrackSelectorResult { * @return Whether this result is equivalent to {@code other} for the renderer at the specified * index. */ - public boolean isEquivalent(TrackSelectorResult other, int index) { + public boolean isEquivalent(@Nullable TrackSelectorResult other, int index) { if (other == null) { return false; }