From b8bb1f642b01306b717ebca78768efd3acc5cfc0 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 24 Jan 2018 03:42:21 -0800 Subject: [PATCH] Move MediaPeriodHolder and MediaPeriodHolderQueue to top level ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=183065262 --- .../exoplayer2/ExoPlayerImplInternal.java | 382 ------------------ .../android/exoplayer2/MediaPeriodHolder.java | 272 +++++++++++++ .../exoplayer2/MediaPeriodHolderQueue.java | 158 ++++++++ 3 files changed, 430 insertions(+), 382 deletions(-) create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolderQueue.java 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 82c7d1eec5..560af6db54 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 @@ -28,14 +28,11 @@ import android.util.Pair; import com.google.android.exoplayer2.DefaultMediaClock.PlaybackParameterListener; import com.google.android.exoplayer2.MediaPeriodInfoSequence.MediaPeriodInfo; import com.google.android.exoplayer2.Player.DiscontinuityReason; -import com.google.android.exoplayer2.source.ClippingMediaPeriod; -import com.google.android.exoplayer2.source.EmptySampleStream; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.trackselection.TrackSelection; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelectorResult; import com.google.android.exoplayer2.util.Assertions; @@ -1688,385 +1685,6 @@ import java.util.Collections; return formats; } - /** - * Holds a queue of {@link MediaPeriodHolder}s from the currently playing period holder at the - * front to the loading period holder at the end of the queue. Also has a reference to the reading - * period holder. - */ - private static final class MediaPeriodHolderQueue { - - private MediaPeriodHolder playing; - private MediaPeriodHolder reading; - private MediaPeriodHolder loading; - private int length; - - /** - * Returns the loading period holder which is at the end of the queue, or null if the queue is - * empty. - */ - public MediaPeriodHolder getLoadingPeriod() { - return loading; - } - - /** - * Returns the playing period holder which is at the front of the queue, or null if the queue is - * empty or hasn't started playing. - */ - public MediaPeriodHolder getPlayingPeriod() { - return playing; - } - - /** - * Returns the reading period holder, or null if the queue is empty or the player hasn't started - * reading. - */ - public MediaPeriodHolder getReadingPeriod() { - return reading; - } - - /** - * Returns the period holder in the front of the queue which is the playing period holder when - * playing, or null if the queue is empty. - */ - public MediaPeriodHolder getFrontPeriod() { - return hasPlayingPeriod() ? playing : loading; - } - - /** Returns the current length of the queue. */ - public int getLength() { - return length; - } - - /** Returns whether the reading and playing period holders are set. */ - public boolean hasPlayingPeriod() { - return playing != null; - } - - /** - * Continues reading from the next period holder in the queue. - * - * @return The updated reading period holder. - */ - public MediaPeriodHolder advanceReadingPeriod() { - Assertions.checkState(reading != null && reading.next != null); - reading = reading.next; - return reading; - } - - /** Enqueues a new period holder at the end, which becomes the new loading period holder. */ - public void enqueueLoadingPeriod(MediaPeriodHolder mediaPeriodHolder) { - Assertions.checkState(mediaPeriodHolder != null); - if (loading != null) { - Assertions.checkState(hasPlayingPeriod()); - loading.next = mediaPeriodHolder; - } - loading = mediaPeriodHolder; - length++; - } - - /** - * Dequeues the playing period holder from the front of the queue and advances the playing - * period holder to be the next item in the queue. If the playing period holder is unset, set it - * to the item in the front of the queue. - * - * @return The updated playing period holder, or null if the queue is or becomes empty. - */ - public MediaPeriodHolder advancePlayingPeriod() { - if (playing != null) { - if (playing == reading) { - reading = playing.next; - } - playing.release(); - playing = playing.next; - length--; - if (length == 0) { - loading = null; - } - } else { - playing = loading; - reading = loading; - } - return playing; - } - - /** - * Removes all period holders after the given period holder. This process may also remove the - * currently reading period holder. If that is the case, the reading period holder is set to be - * the same as the playing period holder at the front of the queue. - * - * @param mediaPeriodHolder The media period holder that shall be the new end of the queue. - * @return Whether the reading period has been removed. - */ - public boolean removeAfter(MediaPeriodHolder mediaPeriodHolder) { - Assertions.checkState(mediaPeriodHolder != null); - boolean removedReading = false; - loading = mediaPeriodHolder; - while (mediaPeriodHolder.next != null) { - mediaPeriodHolder = mediaPeriodHolder.next; - if (mediaPeriodHolder == reading) { - reading = playing; - removedReading = true; - } - mediaPeriodHolder.release(); - length--; - } - loading.next = null; - return removedReading; - } - - /** Clears the queue. */ - public void clear() { - MediaPeriodHolder front = getFrontPeriod(); - if (front != null) { - front.release(); - removeAfter(front); - } - playing = null; - loading = null; - reading = null; - length = 0; - } - } - - /** - * Holds a {@link MediaPeriod} with information required to play it as part of a timeline. - */ - private static final class MediaPeriodHolder { - - public final MediaPeriod mediaPeriod; - public final Object uid; - public final SampleStream[] sampleStreams; - public final boolean[] mayRetainStreamFlags; - - public long rendererPositionOffsetUs; - public MediaPeriodInfo info; - public boolean prepared; - public boolean hasEnabledTracks; - public MediaPeriodHolder next; - public TrackSelectorResult trackSelectorResult; - - private final Renderer[] renderers; - private final RendererCapabilities[] rendererCapabilities; - private final TrackSelector trackSelector; - private final LoadControl loadControl; - private final MediaSource mediaSource; - - private TrackSelectorResult periodTrackSelectorResult; - - public MediaPeriodHolder( - Renderer[] renderers, - RendererCapabilities[] rendererCapabilities, - long rendererPositionOffsetUs, - TrackSelector trackSelector, - LoadControl loadControl, - MediaSource mediaSource, - Object periodUid, - MediaPeriodInfo info) { - this.renderers = renderers; - this.rendererCapabilities = rendererCapabilities; - this.rendererPositionOffsetUs = rendererPositionOffsetUs - info.startPositionUs; - this.trackSelector = trackSelector; - this.loadControl = loadControl; - this.mediaSource = mediaSource; - this.uid = Assertions.checkNotNull(periodUid); - this.info = info; - sampleStreams = new SampleStream[renderers.length]; - mayRetainStreamFlags = new boolean[renderers.length]; - MediaPeriod mediaPeriod = mediaSource.createPeriod(info.id, loadControl.getAllocator()); - if (info.endPositionUs != C.TIME_END_OF_SOURCE) { - ClippingMediaPeriod clippingMediaPeriod = new ClippingMediaPeriod(mediaPeriod, true); - clippingMediaPeriod.setClipping(0, info.endPositionUs); - mediaPeriod = clippingMediaPeriod; - } - this.mediaPeriod = mediaPeriod; - } - - public long toRendererTime(long periodTimeUs) { - return periodTimeUs + getRendererOffset(); - } - - public long toPeriodTime(long rendererTimeUs) { - return rendererTimeUs - getRendererOffset(); - } - - public long getRendererOffset() { - return rendererPositionOffsetUs; - } - - public boolean isFullyBuffered() { - return prepared - && (!hasEnabledTracks || mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE); - } - - public boolean haveSufficientBuffer(long rendererPositionUs, float playbackSpeed, - boolean rebuffering) { - long bufferedPositionUs = !prepared ? info.startPositionUs - : mediaPeriod.getBufferedPositionUs(); - if (bufferedPositionUs == C.TIME_END_OF_SOURCE) { - if (info.isFinal) { - return true; - } - bufferedPositionUs = info.durationUs; - } - return loadControl.shouldStartPlayback(bufferedPositionUs - toPeriodTime(rendererPositionUs), - playbackSpeed, rebuffering); - } - - public void handlePrepared(float playbackSpeed) throws ExoPlaybackException { - prepared = true; - selectTracks(playbackSpeed); - long newStartPositionUs = updatePeriodTrackSelection(info.startPositionUs, false); - rendererPositionOffsetUs += info.startPositionUs - newStartPositionUs; - info = info.copyWithStartPositionUs(newStartPositionUs); - } - - public void reevaluateBuffer(long rendererPositionUs) { - if (prepared) { - mediaPeriod.reevaluateBuffer(toPeriodTime(rendererPositionUs)); - } - } - - public boolean shouldContinueLoading(long rendererPositionUs, float playbackSpeed) { - long nextLoadPositionUs = !prepared ? 0 : mediaPeriod.getNextLoadPositionUs(); - if (nextLoadPositionUs == C.TIME_END_OF_SOURCE) { - return false; - } else { - long bufferedDurationUs = nextLoadPositionUs - toPeriodTime(rendererPositionUs); - return loadControl.shouldContinueLoading(bufferedDurationUs, playbackSpeed); - } - } - - public void continueLoading(long rendererPositionUs) { - long loadingPeriodPositionUs = toPeriodTime(rendererPositionUs); - mediaPeriod.continueLoading(loadingPeriodPositionUs); - } - - public boolean selectTracks(float playbackSpeed) throws ExoPlaybackException { - TrackSelectorResult selectorResult = trackSelector.selectTracks(rendererCapabilities, - mediaPeriod.getTrackGroups()); - if (selectorResult.isEquivalent(periodTrackSelectorResult)) { - return false; - } - trackSelectorResult = selectorResult; - for (TrackSelection trackSelection : trackSelectorResult.selections.getAll()) { - if (trackSelection != null) { - trackSelection.onPlaybackSpeed(playbackSpeed); - } - } - return true; - } - - public long updatePeriodTrackSelection(long positionUs, boolean forceRecreateStreams) { - return updatePeriodTrackSelection(positionUs, forceRecreateStreams, - new boolean[renderers.length]); - } - - public long updatePeriodTrackSelection(long positionUs, boolean forceRecreateStreams, - boolean[] streamResetFlags) { - TrackSelectionArray trackSelections = trackSelectorResult.selections; - for (int i = 0; i < trackSelections.length; i++) { - mayRetainStreamFlags[i] = !forceRecreateStreams - && trackSelectorResult.isEquivalent(periodTrackSelectorResult, 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); - updatePeriodTrackSelectorResult(trackSelectorResult); - // Disable streams on the period and get new streams for updated/newly-enabled tracks. - positionUs = mediaPeriod.selectTracks(trackSelections.getAll(), mayRetainStreamFlags, - sampleStreams, streamResetFlags, positionUs); - associateNoSampleRenderersWithEmptySampleStream(sampleStreams); - - // Update whether we have enabled tracks and sanity check the expected streams are non-null. - hasEnabledTracks = false; - for (int i = 0; i < sampleStreams.length; i++) { - if (sampleStreams[i] != null) { - Assertions.checkState(trackSelectorResult.renderersEnabled[i]); - // hasEnabledTracks should be true only when non-empty streams exists. - if (rendererCapabilities[i].getTrackType() != C.TRACK_TYPE_NONE) { - hasEnabledTracks = true; - } - } else { - Assertions.checkState(trackSelections.get(i) == null); - } - } - // The track selection has changed. - loadControl.onTracksSelected(renderers, trackSelectorResult.groups, trackSelections); - return positionUs; - } - - public void release() { - updatePeriodTrackSelectorResult(null); - try { - if (info.endPositionUs != C.TIME_END_OF_SOURCE) { - mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod); - } else { - mediaSource.releasePeriod(mediaPeriod); - } - } catch (RuntimeException e) { - // There's nothing we can do. - Log.e(TAG, "Period release failed.", e); - } - } - - private void updatePeriodTrackSelectorResult(TrackSelectorResult trackSelectorResult) { - if (periodTrackSelectorResult != null) { - disableTrackSelectionsInResult(periodTrackSelectorResult); - } - periodTrackSelectorResult = trackSelectorResult; - if (periodTrackSelectorResult != null) { - enableTrackSelectionsInResult(periodTrackSelectorResult); - } - } - - private void enableTrackSelectionsInResult(TrackSelectorResult trackSelectorResult) { - for (int i = 0; i < trackSelectorResult.renderersEnabled.length; i++) { - boolean rendererEnabled = trackSelectorResult.renderersEnabled[i]; - TrackSelection trackSelection = trackSelectorResult.selections.get(i); - if (rendererEnabled && trackSelection != null) { - trackSelection.enable(); - } - } - } - - private void disableTrackSelectionsInResult(TrackSelectorResult trackSelectorResult) { - for (int i = 0; i < trackSelectorResult.renderersEnabled.length; i++) { - boolean rendererEnabled = trackSelectorResult.renderersEnabled[i]; - TrackSelection trackSelection = trackSelectorResult.selections.get(i); - if (rendererEnabled && trackSelection != null) { - trackSelection.disable(); - } - } - } - - /** - * 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) { - for (int i = 0; i < rendererCapabilities.length; i++) { - if (rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_NONE) { - sampleStreams[i] = null; - } - } - } - - /** - * 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) { - for (int i = 0; i < rendererCapabilities.length; i++) { - if (rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_NONE - && trackSelectorResult.renderersEnabled[i]) { - sampleStreams[i] = new EmptySampleStream(); - } - } - } - - } - private static final class SeekPosition { public final Timeline timeline; 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 new file mode 100644 index 0000000000..dd4f06bf16 --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2; + +import android.util.Log; +import com.google.android.exoplayer2.MediaPeriodInfoSequence.MediaPeriodInfo; +import com.google.android.exoplayer2.source.ClippingMediaPeriod; +import com.google.android.exoplayer2.source.EmptySampleStream; +import com.google.android.exoplayer2.source.MediaPeriod; +import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.SampleStream; +import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import com.google.android.exoplayer2.trackselection.TrackSelector; +import com.google.android.exoplayer2.trackselection.TrackSelectorResult; +import com.google.android.exoplayer2.util.Assertions; + +/** 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"; + + public final MediaPeriod mediaPeriod; + public final Object uid; + public final SampleStream[] sampleStreams; + public final boolean[] mayRetainStreamFlags; + + public long rendererPositionOffsetUs; + public MediaPeriodInfo info; + public boolean prepared; + public boolean hasEnabledTracks; + public MediaPeriodHolder next; + public TrackSelectorResult trackSelectorResult; + + private final Renderer[] renderers; + private final RendererCapabilities[] rendererCapabilities; + private final TrackSelector trackSelector; + private final LoadControl loadControl; + private final MediaSource mediaSource; + + private TrackSelectorResult periodTrackSelectorResult; + + public MediaPeriodHolder( + Renderer[] renderers, + RendererCapabilities[] rendererCapabilities, + long rendererPositionOffsetUs, + TrackSelector trackSelector, + LoadControl loadControl, + MediaSource mediaSource, + Object periodUid, + MediaPeriodInfo info) { + this.renderers = renderers; + this.rendererCapabilities = rendererCapabilities; + this.rendererPositionOffsetUs = rendererPositionOffsetUs - info.startPositionUs; + this.trackSelector = trackSelector; + this.loadControl = loadControl; + this.mediaSource = mediaSource; + this.uid = Assertions.checkNotNull(periodUid); + this.info = info; + sampleStreams = new SampleStream[renderers.length]; + mayRetainStreamFlags = new boolean[renderers.length]; + MediaPeriod mediaPeriod = mediaSource.createPeriod(info.id, loadControl.getAllocator()); + if (info.endPositionUs != C.TIME_END_OF_SOURCE) { + ClippingMediaPeriod clippingMediaPeriod = new ClippingMediaPeriod(mediaPeriod, true); + clippingMediaPeriod.setClipping(0, info.endPositionUs); + mediaPeriod = clippingMediaPeriod; + } + this.mediaPeriod = mediaPeriod; + } + + public long toRendererTime(long periodTimeUs) { + return periodTimeUs + getRendererOffset(); + } + + public long toPeriodTime(long rendererTimeUs) { + return rendererTimeUs - getRendererOffset(); + } + + public long getRendererOffset() { + return rendererPositionOffsetUs; + } + + public boolean isFullyBuffered() { + return prepared + && (!hasEnabledTracks || mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE); + } + + public boolean haveSufficientBuffer( + long rendererPositionUs, float playbackSpeed, boolean rebuffering) { + long bufferedPositionUs = + !prepared ? info.startPositionUs : mediaPeriod.getBufferedPositionUs(); + if (bufferedPositionUs == C.TIME_END_OF_SOURCE) { + if (info.isFinal) { + return true; + } + bufferedPositionUs = info.durationUs; + } + return loadControl.shouldStartPlayback( + bufferedPositionUs - toPeriodTime(rendererPositionUs), playbackSpeed, rebuffering); + } + + public void handlePrepared(float playbackSpeed) throws ExoPlaybackException { + prepared = true; + selectTracks(playbackSpeed); + long newStartPositionUs = updatePeriodTrackSelection(info.startPositionUs, false); + rendererPositionOffsetUs += info.startPositionUs - newStartPositionUs; + info = info.copyWithStartPositionUs(newStartPositionUs); + } + + public void reevaluateBuffer(long rendererPositionUs) { + if (prepared) { + mediaPeriod.reevaluateBuffer(toPeriodTime(rendererPositionUs)); + } + } + + public boolean shouldContinueLoading(long rendererPositionUs, float playbackSpeed) { + long nextLoadPositionUs = !prepared ? 0 : mediaPeriod.getNextLoadPositionUs(); + if (nextLoadPositionUs == C.TIME_END_OF_SOURCE) { + return false; + } else { + long bufferedDurationUs = nextLoadPositionUs - toPeriodTime(rendererPositionUs); + return loadControl.shouldContinueLoading(bufferedDurationUs, playbackSpeed); + } + } + + public void continueLoading(long rendererPositionUs) { + long loadingPeriodPositionUs = toPeriodTime(rendererPositionUs); + mediaPeriod.continueLoading(loadingPeriodPositionUs); + } + + public boolean selectTracks(float playbackSpeed) throws ExoPlaybackException { + TrackSelectorResult selectorResult = + trackSelector.selectTracks(rendererCapabilities, mediaPeriod.getTrackGroups()); + if (selectorResult.isEquivalent(periodTrackSelectorResult)) { + return false; + } + trackSelectorResult = selectorResult; + for (TrackSelection trackSelection : trackSelectorResult.selections.getAll()) { + if (trackSelection != null) { + trackSelection.onPlaybackSpeed(playbackSpeed); + } + } + return true; + } + + public long updatePeriodTrackSelection(long positionUs, boolean forceRecreateStreams) { + return updatePeriodTrackSelection( + positionUs, forceRecreateStreams, new boolean[renderers.length]); + } + + public long updatePeriodTrackSelection( + long positionUs, boolean forceRecreateStreams, boolean[] streamResetFlags) { + TrackSelectionArray trackSelections = trackSelectorResult.selections; + for (int i = 0; i < trackSelections.length; i++) { + mayRetainStreamFlags[i] = + !forceRecreateStreams && trackSelectorResult.isEquivalent(periodTrackSelectorResult, 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); + updatePeriodTrackSelectorResult(trackSelectorResult); + // Disable streams on the period and get new streams for updated/newly-enabled tracks. + positionUs = + mediaPeriod.selectTracks( + trackSelections.getAll(), + mayRetainStreamFlags, + sampleStreams, + streamResetFlags, + positionUs); + associateNoSampleRenderersWithEmptySampleStream(sampleStreams); + + // Update whether we have enabled tracks and sanity check the expected streams are non-null. + hasEnabledTracks = false; + for (int i = 0; i < sampleStreams.length; i++) { + if (sampleStreams[i] != null) { + Assertions.checkState(trackSelectorResult.renderersEnabled[i]); + // hasEnabledTracks should be true only when non-empty streams exists. + if (rendererCapabilities[i].getTrackType() != C.TRACK_TYPE_NONE) { + hasEnabledTracks = true; + } + } else { + Assertions.checkState(trackSelections.get(i) == null); + } + } + // The track selection has changed. + loadControl.onTracksSelected(renderers, trackSelectorResult.groups, trackSelections); + return positionUs; + } + + public void release() { + updatePeriodTrackSelectorResult(null); + try { + if (info.endPositionUs != C.TIME_END_OF_SOURCE) { + mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod); + } else { + mediaSource.releasePeriod(mediaPeriod); + } + } catch (RuntimeException e) { + // There's nothing we can do. + Log.e(TAG, "Period release failed.", e); + } + } + + private void updatePeriodTrackSelectorResult(TrackSelectorResult trackSelectorResult) { + if (periodTrackSelectorResult != null) { + disableTrackSelectionsInResult(periodTrackSelectorResult); + } + periodTrackSelectorResult = trackSelectorResult; + if (periodTrackSelectorResult != null) { + enableTrackSelectionsInResult(periodTrackSelectorResult); + } + } + + private void enableTrackSelectionsInResult(TrackSelectorResult trackSelectorResult) { + for (int i = 0; i < trackSelectorResult.renderersEnabled.length; i++) { + boolean rendererEnabled = trackSelectorResult.renderersEnabled[i]; + TrackSelection trackSelection = trackSelectorResult.selections.get(i); + if (rendererEnabled && trackSelection != null) { + trackSelection.enable(); + } + } + } + + private void disableTrackSelectionsInResult(TrackSelectorResult trackSelectorResult) { + for (int i = 0; i < trackSelectorResult.renderersEnabled.length; i++) { + boolean rendererEnabled = trackSelectorResult.renderersEnabled[i]; + TrackSelection trackSelection = trackSelectorResult.selections.get(i); + if (rendererEnabled && trackSelection != null) { + trackSelection.disable(); + } + } + } + + /** + * 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) { + for (int i = 0; i < rendererCapabilities.length; i++) { + if (rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_NONE) { + sampleStreams[i] = null; + } + } + } + + /** + * 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) { + for (int i = 0; i < rendererCapabilities.length; i++) { + if (rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_NONE + && trackSelectorResult.renderersEnabled[i]) { + sampleStreams[i] = new EmptySampleStream(); + } + } + } +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolderQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolderQueue.java new file mode 100644 index 0000000000..3504a22d6a --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolderQueue.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2; + +import com.google.android.exoplayer2.util.Assertions; + +/** + * Holds a queue of {@link MediaPeriodHolder}s from the currently playing period holder at the front + * to the loading period holder at the end of the queue. Also has a reference to the reading period + * holder. + */ +/* package */ final class MediaPeriodHolderQueue { + + private MediaPeriodHolder playing; + private MediaPeriodHolder reading; + private MediaPeriodHolder loading; + private int length; + + /** + * Returns the loading period holder which is at the end of the queue, or null if the queue is + * empty. + */ + public MediaPeriodHolder getLoadingPeriod() { + return loading; + } + + /** + * Returns the playing period holder which is at the front of the queue, or null if the queue is + * empty or hasn't started playing. + */ + public MediaPeriodHolder getPlayingPeriod() { + return playing; + } + + /** + * Returns the reading period holder, or null if the queue is empty or the player hasn't started + * reading. + */ + public MediaPeriodHolder getReadingPeriod() { + return reading; + } + + /** + * Returns the period holder in the front of the queue which is the playing period holder when + * playing, or null if the queue is empty. + */ + public MediaPeriodHolder getFrontPeriod() { + return hasPlayingPeriod() ? playing : loading; + } + + /** Returns the current length of the queue. */ + public int getLength() { + return length; + } + + /** Returns whether the reading and playing period holders are set. */ + public boolean hasPlayingPeriod() { + return playing != null; + } + + /** + * Continues reading from the next period holder in the queue. + * + * @return The updated reading period holder. + */ + public MediaPeriodHolder advanceReadingPeriod() { + Assertions.checkState(reading != null && reading.next != null); + reading = reading.next; + return reading; + } + + /** Enqueues a new period holder at the end, which becomes the new loading period holder. */ + public void enqueueLoadingPeriod(MediaPeriodHolder mediaPeriodHolder) { + Assertions.checkState(mediaPeriodHolder != null); + if (loading != null) { + Assertions.checkState(hasPlayingPeriod()); + loading.next = mediaPeriodHolder; + } + loading = mediaPeriodHolder; + length++; + } + + /** + * Dequeues the playing period holder from the front of the queue and advances the playing period + * holder to be the next item in the queue. If the playing period holder is unset, set it to the + * item in the front of the queue. + * + * @return The updated playing period holder, or null if the queue is or becomes empty. + */ + public MediaPeriodHolder advancePlayingPeriod() { + if (playing != null) { + if (playing == reading) { + reading = playing.next; + } + playing.release(); + playing = playing.next; + length--; + if (length == 0) { + loading = null; + } + } else { + playing = loading; + reading = loading; + } + return playing; + } + + /** + * Removes all period holders after the given period holder. This process may also remove the + * currently reading period holder. If that is the case, the reading period holder is set to be + * the same as the playing period holder at the front of the queue. + * + * @param mediaPeriodHolder The media period holder that shall be the new end of the queue. + * @return Whether the reading period has been removed. + */ + public boolean removeAfter(MediaPeriodHolder mediaPeriodHolder) { + Assertions.checkState(mediaPeriodHolder != null); + boolean removedReading = false; + loading = mediaPeriodHolder; + while (mediaPeriodHolder.next != null) { + mediaPeriodHolder = mediaPeriodHolder.next; + if (mediaPeriodHolder == reading) { + reading = playing; + removedReading = true; + } + mediaPeriodHolder.release(); + length--; + } + loading.next = null; + return removedReading; + } + + /** Clears the queue. */ + public void clear() { + MediaPeriodHolder front = getFrontPeriod(); + if (front != null) { + front.release(); + removeAfter(front); + } + playing = null; + loading = null; + reading = null; + length = 0; + } +}