Clarify and improve TrackSelection .enable() and .disable() calls.

These two methods are meant to indicate to the track selection that it's
started or stopped being used. This is helpful to schedule background tasks
related to track selection (e.g. register network change listeners etc.).
This intention is not clearly stated in the method docs.

Also, all track selections of all prebuffered periods stay enabled in
parallel at the moment. As the whole purpose of these methods is to know
whether dynamic updates via updateSelectedTrack may happen, it's better to
only enable track selections of the current loading media period. That's
similar to how we always forward the loading track selections to the
LoadControl.

This change:
 1. Improves the JavaDoc of TrackSelection.
 2. Disables track selections if loading moves to another period.
 3. Reenables track selection if loading moves back to a previous period.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=215199987
This commit is contained in:
tonihei 2018-10-01 05:45:24 -07:00 committed by Oliver Woodman
parent f59f557704
commit d97d289b6b
4 changed files with 84 additions and 57 deletions

View File

@ -1006,7 +1006,7 @@ import java.util.Collections;
// The track reselection didn't affect any period that has been read.
selectionsChangedForReadPeriod = false;
}
periodHolder = periodHolder.next;
periodHolder = periodHolder.getNext();
}
if (selectionsChangedForReadPeriod) {
@ -1078,7 +1078,7 @@ import java.util.Collections;
}
}
}
periodHolder = periodHolder.next;
periodHolder = periodHolder.getNext();
}
}
@ -1107,11 +1107,12 @@ import java.util.Collections;
private boolean isTimelineReady() {
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
MediaPeriodHolder nextPeriodHolder = playingPeriodHolder.getNext();
long playingPeriodDurationUs = playingPeriodHolder.info.durationUs;
return playingPeriodDurationUs == C.TIME_UNSET
|| playbackInfo.positionUs < playingPeriodDurationUs
|| (playingPeriodHolder.next != null
&& (playingPeriodHolder.next.prepared || playingPeriodHolder.next.info.id.isAd()));
|| (nextPeriodHolder != null
&& (nextPeriodHolder.prepared || nextPeriodHolder.info.id.isAd()));
}
private void maybeThrowSourceInfoRefreshError() throws IOException {
@ -1130,8 +1131,9 @@ import java.util.Collections;
private void maybeThrowPeriodPrepareError() throws IOException {
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
if (loadingPeriodHolder != null && !loadingPeriodHolder.prepared
&& (readingPeriodHolder == null || readingPeriodHolder.next == loadingPeriodHolder)) {
if (loadingPeriodHolder != null
&& !loadingPeriodHolder.prepared
&& (readingPeriodHolder == null || readingPeriodHolder.getNext() == loadingPeriodHolder)) {
for (Renderer renderer : enabledRenderers) {
if (!renderer.hasReadStreamToEnd()) {
return;
@ -1241,8 +1243,8 @@ import java.util.Collections;
MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(newPeriodUid, contentPositionUs);
if (periodHolder != null) {
// Update the new playing media period info if it already exists.
while (periodHolder.next != null) {
periodHolder = periodHolder.next;
while (periodHolder.getNext() != null) {
periodHolder = periodHolder.getNext();
if (periodHolder.info.id.equals(periodId)) {
periodHolder.info = queue.getUpdatedMediaPeriodInfo(periodHolder.info);
}
@ -1404,7 +1406,7 @@ import java.util.Collections;
boolean advancedPlayingPeriod = false;
while (playWhenReady
&& playingPeriodHolder != readingPeriodHolder
&& rendererPositionUs >= playingPeriodHolder.next.getStartPositionRendererTime()) {
&& rendererPositionUs >= playingPeriodHolder.getNext().getStartPositionRendererTime()) {
// All enabled renderers' streams have been read to the end, and the playback position reached
// the end of the playing period, so advance playback to the next period.
if (advancedPlayingPeriod) {
@ -1440,7 +1442,7 @@ import java.util.Collections;
}
// Advance the reading period if necessary.
if (readingPeriodHolder.next == null) {
if (readingPeriodHolder.getNext() == null) {
// We don't have a successor to advance the reading period to.
return;
}
@ -1455,7 +1457,7 @@ import java.util.Collections;
}
}
if (!readingPeriodHolder.next.prepared) {
if (!readingPeriodHolder.getNext().prepared) {
// The successor is not prepared yet.
maybeThrowPeriodPrepareError();
return;
@ -1655,8 +1657,8 @@ import java.util.Collections;
private boolean rendererWaitingForNextStream(Renderer renderer) {
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
return readingPeriodHolder.next != null && readingPeriodHolder.next.prepared
&& renderer.hasReadStreamToEnd();
MediaPeriodHolder nextPeriodHolder = readingPeriodHolder.getNext();
return nextPeriodHolder != null && nextPeriodHolder.prepared && renderer.hasReadStreamToEnd();
}
private void handleLoadingMediaPeriodChanged(boolean loadingTrackSelectionChanged) {

View File

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.source.ClippingMediaPeriod;
import com.google.android.exoplayer2.source.EmptySampleStream;
import com.google.android.exoplayer2.source.MediaPeriod;
@ -42,7 +43,6 @@ import com.google.android.exoplayer2.util.Log;
public boolean prepared;
public boolean hasEnabledTracks;
public MediaPeriodInfo info;
public MediaPeriodHolder next;
public TrackGroupArray trackGroups;
public TrackSelectorResult trackSelectorResult;
@ -50,6 +50,7 @@ import com.google.android.exoplayer2.util.Log;
private final TrackSelector trackSelector;
private final MediaSource mediaSource;
private MediaPeriodHolder next;
private long rendererPositionOffsetUs;
private TrackSelectorResult periodTrackSelectorResult;
@ -190,7 +191,9 @@ import com.google.android.exoplayer2.util.Log;
// Undo the effect of previous call to associate no-sample renderers with empty tracks
// so the mediaPeriod receives back whatever it sent us before.
disassociateNoSampleRenderersWithEmptySampleStream(sampleStreams);
updatePeriodTrackSelectorResult(trackSelectorResult);
disableTrackSelectionsInResult();
periodTrackSelectorResult = trackSelectorResult;
enableTrackSelectionsInResult();
// Disable streams on the period and get new streams for updated/newly-enabled tracks.
TrackSelectionArray trackSelections = trackSelectorResult.selections;
positionUs =
@ -219,7 +222,8 @@ import com.google.android.exoplayer2.util.Log;
}
public void release() {
updatePeriodTrackSelectorResult(null);
disableTrackSelectionsInResult();
periodTrackSelectorResult = null;
try {
if (info.id.endPositionUs != C.TIME_END_OF_SOURCE) {
mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod);
@ -232,30 +236,40 @@ import com.google.android.exoplayer2.util.Log;
}
}
private void updatePeriodTrackSelectorResult(TrackSelectorResult trackSelectorResult) {
if (periodTrackSelectorResult != null) {
disableTrackSelectionsInResult(periodTrackSelectorResult);
}
periodTrackSelectorResult = trackSelectorResult;
if (periodTrackSelectorResult != null) {
enableTrackSelectionsInResult(periodTrackSelectorResult);
public void setNext(@Nullable MediaPeriodHolder nextMediaPeriodHolder) {
if (nextMediaPeriodHolder == next) {
return;
}
disableTrackSelectionsInResult();
next = nextMediaPeriodHolder;
enableTrackSelectionsInResult();
}
private void enableTrackSelectionsInResult(TrackSelectorResult trackSelectorResult) {
for (int i = 0; i < trackSelectorResult.length; i++) {
boolean rendererEnabled = trackSelectorResult.isRendererEnabled(i);
TrackSelection trackSelection = trackSelectorResult.selections.get(i);
@Nullable
public MediaPeriodHolder getNext() {
return next;
}
private void enableTrackSelectionsInResult() {
if (!isLoadingMediaPeriod() || periodTrackSelectorResult == null) {
return;
}
for (int i = 0; i < periodTrackSelectorResult.length; i++) {
boolean rendererEnabled = periodTrackSelectorResult.isRendererEnabled(i);
TrackSelection trackSelection = periodTrackSelectorResult.selections.get(i);
if (rendererEnabled && trackSelection != null) {
trackSelection.enable();
}
}
}
private void disableTrackSelectionsInResult(TrackSelectorResult trackSelectorResult) {
for (int i = 0; i < trackSelectorResult.length; i++) {
boolean rendererEnabled = trackSelectorResult.isRendererEnabled(i);
TrackSelection trackSelection = trackSelectorResult.selections.get(i);
private void disableTrackSelectionsInResult() {
if (!isLoadingMediaPeriod() || periodTrackSelectorResult == null) {
return;
}
for (int i = 0; i < periodTrackSelectorResult.length; i++) {
boolean rendererEnabled = periodTrackSelectorResult.isRendererEnabled(i);
TrackSelection trackSelection = periodTrackSelectorResult.selections.get(i);
if (rendererEnabled && trackSelection != null) {
trackSelection.disable();
}
@ -286,4 +300,8 @@ import com.google.android.exoplayer2.util.Log;
}
}
}
private boolean isLoadingMediaPeriod() {
return next == null;
}
}

View File

@ -156,7 +156,7 @@ import com.google.android.exoplayer2.util.Assertions;
info);
if (loading != null) {
Assertions.checkState(hasPlayingPeriod());
loading.next = newPeriodHolder;
loading.setNext(newPeriodHolder);
}
oldFrontPeriodUid = null;
loading = newPeriodHolder;
@ -207,8 +207,8 @@ import com.google.android.exoplayer2.util.Assertions;
* @return The updated reading period holder.
*/
public MediaPeriodHolder advanceReadingPeriod() {
Assertions.checkState(reading != null && reading.next != null);
reading = reading.next;
Assertions.checkState(reading != null && reading.getNext() != null);
reading = reading.getNext();
return reading;
}
@ -222,7 +222,7 @@ import com.google.android.exoplayer2.util.Assertions;
public MediaPeriodHolder advancePlayingPeriod() {
if (playing != null) {
if (playing == reading) {
reading = playing.next;
reading = playing.getNext();
}
playing.release();
length--;
@ -231,7 +231,7 @@ import com.google.android.exoplayer2.util.Assertions;
oldFrontPeriodUid = playing.uid;
oldFrontPeriodWindowSequenceNumber = playing.info.id.windowSequenceNumber;
}
playing = playing.next;
playing = playing.getNext();
} else {
playing = loading;
reading = loading;
@ -251,8 +251,8 @@ import com.google.android.exoplayer2.util.Assertions;
Assertions.checkState(mediaPeriodHolder != null);
boolean removedReading = false;
loading = mediaPeriodHolder;
while (mediaPeriodHolder.next != null) {
mediaPeriodHolder = mediaPeriodHolder.next;
while (mediaPeriodHolder.getNext() != null) {
mediaPeriodHolder = mediaPeriodHolder.getNext();
if (mediaPeriodHolder == reading) {
reading = playing;
removedReading = true;
@ -260,7 +260,7 @@ import com.google.android.exoplayer2.util.Assertions;
mediaPeriodHolder.release();
length--;
}
loading.next = null;
loading.setNext(null);
return removedReading;
}
@ -337,7 +337,7 @@ import com.google.android.exoplayer2.util.Assertions;
}
previousPeriodHolder = periodHolder;
periodHolder = periodHolder.next;
periodHolder = periodHolder.getNext();
}
return true;
}
@ -439,7 +439,7 @@ import com.google.android.exoplayer2.util.Assertions;
// Reuse window sequence number of first exact period match.
return mediaPeriodHolder.info.id.windowSequenceNumber;
}
mediaPeriodHolder = mediaPeriodHolder.next;
mediaPeriodHolder = mediaPeriodHolder.getNext();
}
mediaPeriodHolder = getFrontPeriod();
while (mediaPeriodHolder != null) {
@ -451,7 +451,7 @@ import com.google.android.exoplayer2.util.Assertions;
return mediaPeriodHolder.info.id.windowSequenceNumber;
}
}
mediaPeriodHolder = mediaPeriodHolder.next;
mediaPeriodHolder = mediaPeriodHolder.getNext();
}
// If no match is found, create new sequence number.
return nextWindowSequenceNumber++;
@ -482,19 +482,20 @@ import com.google.android.exoplayer2.util.Assertions;
int nextPeriodIndex =
timeline.getNextPeriodIndex(
currentPeriodIndex, period, window, repeatMode, shuffleModeEnabled);
while (lastValidPeriodHolder.next != null
while (lastValidPeriodHolder.getNext() != null
&& !lastValidPeriodHolder.info.isLastInTimelinePeriod) {
lastValidPeriodHolder = lastValidPeriodHolder.next;
lastValidPeriodHolder = lastValidPeriodHolder.getNext();
}
if (nextPeriodIndex == C.INDEX_UNSET || lastValidPeriodHolder.next == null) {
MediaPeriodHolder nextMediaPeriodHolder = lastValidPeriodHolder.getNext();
if (nextPeriodIndex == C.INDEX_UNSET || nextMediaPeriodHolder == null) {
break;
}
int nextPeriodHolderPeriodIndex = timeline.getIndexOfPeriod(lastValidPeriodHolder.next.uid);
int nextPeriodHolderPeriodIndex = timeline.getIndexOfPeriod(nextMediaPeriodHolder.uid);
if (nextPeriodHolderPeriodIndex != nextPeriodIndex) {
break;
}
lastValidPeriodHolder = lastValidPeriodHolder.next;
lastValidPeriodHolder = nextMediaPeriodHolder;
currentPeriodIndex = nextPeriodIndex;
}
@ -567,8 +568,9 @@ import com.google.android.exoplayer2.util.Assertions;
}
nextPeriodUid = defaultPosition.first;
startPositionUs = defaultPosition.second;
if (mediaPeriodHolder.next != null && mediaPeriodHolder.next.uid.equals(nextPeriodUid)) {
windowSequenceNumber = mediaPeriodHolder.next.info.id.windowSequenceNumber;
MediaPeriodHolder nextMediaPeriodHolder = mediaPeriodHolder.getNext();
if (nextMediaPeriodHolder != null && nextMediaPeriodHolder.uid.equals(nextPeriodUid)) {
windowSequenceNumber = nextMediaPeriodHolder.info.id.windowSequenceNumber;
} else {
windowSequenceNumber = nextWindowSequenceNumber++;
}

View File

@ -29,8 +29,9 @@ import java.util.List;
* TrackGroup}, and a possibly varying individual selected track from the subset.
*
* <p>Tracks belonging to the subset are exposed in decreasing bandwidth order. The individual
* selected track may change as a result of calling {@link #updateSelectedTrack(long, long, long,
* List, MediaChunkIterator[])}.
* selected track may change dynamically as a result of calling {@link #updateSelectedTrack(long,
* long, long, List, MediaChunkIterator[])} or {@link #evaluateQueueSize(long, List)}. This only
* happens between calls to {@link #enable()} and {@link #disable()}.
*/
public interface TrackSelection {
@ -53,16 +54,20 @@ public interface TrackSelection {
}
/**
* Enables the track selection.
* <p>
* This method may not be called when the track selection is already enabled.
* Enables the track selection. Dynamic changes via {@link #updateSelectedTrack(long, long, long,
* List, MediaChunkIterator[])} or {@link #evaluateQueueSize(long, List)} will only happen after
* this call.
*
* <p>This method may not be called when the track selection is already enabled.
*/
void enable();
/**
* Disables this track selection.
* <p>
* This method may only be called when the track selection is already enabled.
* Disables this track selection. No further dynamic changes via {@link #updateSelectedTrack(long,
* long, long, List, MediaChunkIterator[])} or {@link #evaluateQueueSize(long, List)} will happen
* after this call.
*
* <p>This method may only be called when the track selection is already enabled.
*/
void disable();