Commit 652c2f9c by tonihei Committed by Toni

Advance playing period even if the next one isn't prepared yet.

This solves various issues around event association for buffering and
error throwing around period discontinuities.

The main changes are:
 - Logic around being "ready" at the end of a period no longer checks if the
   next period is prepared.
 - Advancing the playing period no longer checks if the next one is prepared.
 - Prepare errors are always thrown for the playing period.

This changes the semantics and assumptions about the "playing" period:
 1. The playing period can no longer assumed to be prepared.
 2. We no longer have a case where the queue is non-empty and the playing or
    reading periods are unassigned (=null).
Most other code changes ensure that these changed assumptions are handled.

Issue:#5407
PiperOrigin-RevId: 263776304
parent 14f77cb8
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
`ExoPlayer.Builder`. `ExoPlayer.Builder`.
* Inject `DrmSessionManager` into the `MediaSources` instead of `Renderers` * Inject `DrmSessionManager` into the `MediaSources` instead of `Renderers`
([#5619](https://github.com/google/ExoPlayer/issues/5619)). ([#5619](https://github.com/google/ExoPlayer/issues/5619)).
* Fix issue where player errors are thrown too early at playlist transitions
([#5407](https://github.com/google/ExoPlayer/issues/5407)).
### 2.10.4 ### ### 2.10.4 ###
......
...@@ -128,8 +128,8 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -128,8 +128,8 @@ import com.google.android.exoplayer2.util.Assertions;
} }
/** /**
* Enqueues a new media period based on the specified information as the new loading media period, * Enqueues a new media period holder based on the specified information as the new loading media
* and returns it. * period, and returns it.
* *
* @param rendererCapabilities The renderer capabilities. * @param rendererCapabilities The renderer capabilities.
* @param trackSelector The track selector. * @param trackSelector The track selector.
...@@ -139,7 +139,7 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -139,7 +139,7 @@ import com.google.android.exoplayer2.util.Assertions;
* @param emptyTrackSelectorResult A {@link TrackSelectorResult} with empty selections for each * @param emptyTrackSelectorResult A {@link TrackSelectorResult} with empty selections for each
* renderer. * renderer.
*/ */
public MediaPeriod enqueueNextMediaPeriod( public MediaPeriodHolder enqueueNextMediaPeriodHolder(
RendererCapabilities[] rendererCapabilities, RendererCapabilities[] rendererCapabilities,
TrackSelector trackSelector, TrackSelector trackSelector,
Allocator allocator, Allocator allocator,
...@@ -162,13 +162,15 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -162,13 +162,15 @@ import com.google.android.exoplayer2.util.Assertions;
info, info,
emptyTrackSelectorResult); emptyTrackSelectorResult);
if (loading != null) { if (loading != null) {
Assertions.checkState(hasPlayingPeriod());
loading.setNext(newPeriodHolder); loading.setNext(newPeriodHolder);
} else {
playing = newPeriodHolder;
reading = newPeriodHolder;
} }
oldFrontPeriodUid = null; oldFrontPeriodUid = null;
loading = newPeriodHolder; loading = newPeriodHolder;
length++; length++;
return newPeriodHolder.mediaPeriod; return newPeriodHolder;
} }
/** /**
...@@ -182,37 +184,20 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -182,37 +184,20 @@ import com.google.android.exoplayer2.util.Assertions;
/** /**
* Returns the playing period holder which is at the front of the queue, or null if the queue is * 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. * empty.
*/ */
@Nullable @Nullable
public MediaPeriodHolder getPlayingPeriod() { public MediaPeriodHolder getPlayingPeriod() {
return playing; return playing;
} }
/** /** Returns the reading period holder, or null if the queue is empty. */
* Returns the reading period holder, or null if the queue is empty or the player hasn't started
* reading.
*/
@Nullable @Nullable
public MediaPeriodHolder getReadingPeriod() { public MediaPeriodHolder getReadingPeriod() {
return reading; 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.
*/
@Nullable
public MediaPeriodHolder getFrontPeriod() {
return hasPlayingPeriod() ? playing : loading;
}
/** 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. * Continues reading from the next period holder in the queue.
* *
* @return The updated reading period holder. * @return The updated reading period holder.
...@@ -225,29 +210,26 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -225,29 +210,26 @@ import com.google.android.exoplayer2.util.Assertions;
/** /**
* Dequeues the playing period holder from the front of the queue and advances the playing period * 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 * holder to be the next item in the queue.
* item in the front of the queue.
* *
* @return The updated playing period holder, or null if the queue is or becomes empty. * @return The updated playing period holder, or null if the queue is or becomes empty.
*/ */
@Nullable @Nullable
public MediaPeriodHolder advancePlayingPeriod() { public MediaPeriodHolder advancePlayingPeriod() {
if (playing != null) { if (playing == null) {
if (playing == reading) { return null;
reading = playing.getNext(); }
} if (playing == reading) {
playing.release(); reading = playing.getNext();
length--; }
if (length == 0) { playing.release();
loading = null; length--;
oldFrontPeriodUid = playing.uid; if (length == 0) {
oldFrontPeriodWindowSequenceNumber = playing.info.id.windowSequenceNumber; loading = null;
} oldFrontPeriodUid = playing.uid;
playing = playing.getNext(); oldFrontPeriodWindowSequenceNumber = playing.info.id.windowSequenceNumber;
} else {
playing = loading;
reading = loading;
} }
playing = playing.getNext();
return playing; return playing;
} }
...@@ -283,7 +265,7 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -283,7 +265,7 @@ import com.google.android.exoplayer2.util.Assertions;
* of queue (typically the playing one) for later reuse. * of queue (typically the playing one) for later reuse.
*/ */
public void clear(boolean keepFrontPeriodUid) { public void clear(boolean keepFrontPeriodUid) {
MediaPeriodHolder front = getFrontPeriod(); MediaPeriodHolder front = playing;
if (front != null) { if (front != null) {
oldFrontPeriodUid = keepFrontPeriodUid ? front.uid : null; oldFrontPeriodUid = keepFrontPeriodUid ? front.uid : null;
oldFrontPeriodWindowSequenceNumber = front.info.id.windowSequenceNumber; oldFrontPeriodWindowSequenceNumber = front.info.id.windowSequenceNumber;
...@@ -315,7 +297,7 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -315,7 +297,7 @@ import com.google.android.exoplayer2.util.Assertions;
// is set, once all cases handled by ExoPlayerImplInternal.handleSourceInfoRefreshed can be // is set, once all cases handled by ExoPlayerImplInternal.handleSourceInfoRefreshed can be
// handled here. // handled here.
MediaPeriodHolder previousPeriodHolder = null; MediaPeriodHolder previousPeriodHolder = null;
MediaPeriodHolder periodHolder = getFrontPeriod(); MediaPeriodHolder periodHolder = playing;
while (periodHolder != null) { while (periodHolder != null) {
MediaPeriodInfo oldPeriodInfo = periodHolder.info; MediaPeriodInfo oldPeriodInfo = periodHolder.info;
...@@ -451,7 +433,7 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -451,7 +433,7 @@ import com.google.android.exoplayer2.util.Assertions;
} }
} }
} }
MediaPeriodHolder mediaPeriodHolder = getFrontPeriod(); MediaPeriodHolder mediaPeriodHolder = playing;
while (mediaPeriodHolder != null) { while (mediaPeriodHolder != null) {
if (mediaPeriodHolder.uid.equals(periodUid)) { if (mediaPeriodHolder.uid.equals(periodUid)) {
// Reuse window sequence number of first exact period match. // Reuse window sequence number of first exact period match.
...@@ -459,7 +441,7 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -459,7 +441,7 @@ import com.google.android.exoplayer2.util.Assertions;
} }
mediaPeriodHolder = mediaPeriodHolder.getNext(); mediaPeriodHolder = mediaPeriodHolder.getNext();
} }
mediaPeriodHolder = getFrontPeriod(); mediaPeriodHolder = playing;
while (mediaPeriodHolder != null) { while (mediaPeriodHolder != null) {
int indexOfHolderInTimeline = timeline.getIndexOfPeriod(mediaPeriodHolder.uid); int indexOfHolderInTimeline = timeline.getIndexOfPeriod(mediaPeriodHolder.uid);
if (indexOfHolderInTimeline != C.INDEX_UNSET) { if (indexOfHolderInTimeline != C.INDEX_UNSET) {
...@@ -496,7 +478,7 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -496,7 +478,7 @@ import com.google.android.exoplayer2.util.Assertions;
*/ */
private boolean updateForPlaybackModeChange() { private boolean updateForPlaybackModeChange() {
// Find the last existing period holder that matches the new period order. // Find the last existing period holder that matches the new period order.
MediaPeriodHolder lastValidPeriodHolder = getFrontPeriod(); MediaPeriodHolder lastValidPeriodHolder = playing;
if (lastValidPeriodHolder == null) { if (lastValidPeriodHolder == null) {
return true; return true;
} }
...@@ -529,7 +511,7 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -529,7 +511,7 @@ import com.google.android.exoplayer2.util.Assertions;
lastValidPeriodHolder.info = getUpdatedMediaPeriodInfo(lastValidPeriodHolder.info); lastValidPeriodHolder.info = getUpdatedMediaPeriodInfo(lastValidPeriodHolder.info);
// If renderers may have read from a period that's been removed, it is necessary to restart. // If renderers may have read from a period that's been removed, it is necessary to restart.
return !readingPeriodRemoved || !hasPlayingPeriod(); return !readingPeriodRemoved;
} }
/** /**
......
...@@ -370,7 +370,9 @@ public final class MediaPeriodQueueTest { ...@@ -370,7 +370,9 @@ public final class MediaPeriodQueueTest {
private void advance() { private void advance() {
enqueueNext(); enqueueNext();
advancePlaying(); if (mediaPeriodQueue.getLoadingPeriod() != mediaPeriodQueue.getPlayingPeriod()) {
advancePlaying();
}
} }
private void advancePlaying() { private void advancePlaying() {
...@@ -382,7 +384,7 @@ public final class MediaPeriodQueueTest { ...@@ -382,7 +384,7 @@ public final class MediaPeriodQueueTest {
} }
private void enqueueNext() { private void enqueueNext() {
mediaPeriodQueue.enqueueNextMediaPeriod( mediaPeriodQueue.enqueueNextMediaPeriodHolder(
rendererCapabilities, rendererCapabilities,
trackSelector, trackSelector,
allocator, allocator,
...@@ -460,7 +462,7 @@ public final class MediaPeriodQueueTest { ...@@ -460,7 +462,7 @@ public final class MediaPeriodQueueTest {
private int getQueueLength() { private int getQueueLength() {
int length = 0; int length = 0;
MediaPeriodHolder periodHolder = mediaPeriodQueue.getFrontPeriod(); MediaPeriodHolder periodHolder = mediaPeriodQueue.getPlayingPeriod();
while (periodHolder != null) { while (periodHolder != null) {
length++; length++;
periodHolder = periodHolder.getNext(); periodHolder = periodHolder.getNext();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment