提交 b8bb1f64 编写于 作者: A andrewlewis 提交者: Oliver Woodman

Move MediaPeriodHolder and MediaPeriodHolderQueue to top level

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=183065262
上级 c8298e70
......@@ -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;
......
/*
* 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();
}
}
}
}
/*
* 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;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册