Use time-to-first-byte in AdaptiveTrackSelection.

Knowing the time-to-first-byte allows to update the available
allocatable bandwidth to take this into account.

PiperOrigin-RevId: 363225573
This commit is contained in:
tonihei 2021-03-16 18:12:20 +00:00 committed by Ian Baker
parent 57796914a7
commit 3e613beb78
2 changed files with 151 additions and 50 deletions

View file

@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.trackselection;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
@ -318,11 +317,12 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
List<? extends MediaChunk> queue,
MediaChunkIterator[] mediaChunkIterators) {
long nowMs = clock.elapsedRealtime();
long chunkDurationUs = getChunkDurationUs(mediaChunkIterators, queue);
// Make initial selection
if (reason == C.SELECTION_REASON_UNKNOWN) {
reason = C.SELECTION_REASON_INITIAL;
selectedIndex = determineIdealSelectedIndex(nowMs);
selectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs);
return;
}
@ -334,7 +334,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
previousSelectedIndex = formatIndexOfPreviousChunk;
previousReason = Iterables.getLast(queue).trackSelectionReason;
}
int newSelectedIndex = determineIdealSelectedIndex(nowMs);
int newSelectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs);
if (!isBlacklisted(previousSelectedIndex, nowMs)) {
// Revert back to the previous selection if conditions are not suitable for switching.
Format currentFormat = getFormat(previousSelectedIndex);
@ -394,7 +394,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
if (playoutBufferedDurationBeforeLastChunkUs < minDurationToRetainAfterDiscardUs) {
return queueSize;
}
int idealSelectedIndex = determineIdealSelectedIndex(nowMs);
int idealSelectedIndex = determineIdealSelectedIndex(nowMs, getChunkDurationUs(queue));
Format idealFormat = getFormat(idealSelectedIndex);
// If the chunks contain video, discard from the first SD chunk beyond
// minDurationToRetainAfterDiscardUs whose resolution and bitrate are both lower than the ideal
@ -459,9 +459,11 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
*
* @param nowMs The current time in the timebase of {@link Clock#elapsedRealtime()}, or {@link
* Long#MIN_VALUE} to ignore track exclusion.
* @param chunkDurationUs The duration of a media chunk in microseconds, or {@link C#TIME_UNSET}
* if unknown.
*/
private int determineIdealSelectedIndex(long nowMs) {
long effectiveBitrate = getAllocatedBandwidth();
private int determineIdealSelectedIndex(long nowMs, long chunkDurationUs) {
long effectiveBitrate = getAllocatedBandwidth(chunkDurationUs);
int lowestBitrateAllowedIndex = 0;
for (int i = 0; i < length; i++) {
if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) {
@ -484,9 +486,35 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
: minDurationForQualityIncreaseUs;
}
private long getAllocatedBandwidth() {
long totalBandwidth =
(long) (bandwidthMeter.getBitrateEstimate() * bandwidthFraction / playbackSpeed);
private long getChunkDurationUs(
MediaChunkIterator[] mediaChunkIterators, List<? extends MediaChunk> queue) {
// First, try to get the chunk duration for currently selected format.
if (selectedIndex < mediaChunkIterators.length && mediaChunkIterators[selectedIndex].next()) {
MediaChunkIterator iterator = mediaChunkIterators[selectedIndex];
return iterator.getChunkEndTimeUs() - iterator.getChunkStartTimeUs();
}
// Second, try to get the chunk duration for another format.
for (MediaChunkIterator iterator : mediaChunkIterators) {
if (iterator.next()) {
return iterator.getChunkEndTimeUs() - iterator.getChunkStartTimeUs();
}
}
// Third, try to get chunk duration for previous chunk in the queue.
return getChunkDurationUs(queue);
}
private long getChunkDurationUs(List<? extends MediaChunk> queue) {
if (queue.isEmpty()) {
return C.TIME_UNSET;
}
MediaChunk lastChunk = Iterables.getLast(queue);
return lastChunk.startTimeUs != C.TIME_UNSET && lastChunk.endTimeUs != C.TIME_UNSET
? lastChunk.endTimeUs - lastChunk.startTimeUs
: C.TIME_UNSET;
}
private long getAllocatedBandwidth(long chunkDurationUs) {
long totalBandwidth = getTotalAllocatableBandwidth(chunkDurationUs);
if (adaptationCheckpoints.isEmpty()) {
return totalBandwidth;
}
@ -505,6 +533,17 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
(fractionBetweenCheckpoints * (next.allocatedBandwidth - previous.allocatedBandwidth));
}
private long getTotalAllocatableBandwidth(long chunkDurationUs) {
long cautiousBandwidthEstimate =
(long) (bandwidthMeter.getBitrateEstimate() * bandwidthFraction);
long timeToFirstByteEstimateUs = bandwidthMeter.getTimeToFirstByteEstimateUs();
if (timeToFirstByteEstimateUs == C.TIME_UNSET || chunkDurationUs == C.TIME_UNSET) {
return (long) (cautiousBandwidthEstimate / playbackSpeed);
}
float availableTimeToLoadUs = chunkDurationUs / playbackSpeed - timeToFirstByteEstimateUs;
return (long) (cautiousBandwidthEstimate * availableTimeToLoadUs / chunkDurationUs);
}
/**
* Returns adaptation checkpoints for allocating bandwidth for adaptive track selections.
*

View file

@ -25,6 +25,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.chunk.BaseMediaChunkIterator;
import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
import com.google.android.exoplayer2.testutil.FakeClock;
import com.google.android.exoplayer2.testutil.FakeMediaChunk;
@ -32,6 +33,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection.AdaptationCheckpoint;
import com.google.android.exoplayer2.trackselection.ExoTrackSelection.Definition;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@ -46,10 +48,7 @@ import org.mockito.Mock;
@RunWith(AndroidJUnit4.class)
public final class AdaptiveTrackSelectionTest {
private static final MediaChunkIterator[] THREE_EMPTY_MEDIA_CHUNK_ITERATORS =
new MediaChunkIterator[] {
MediaChunkIterator.EMPTY, MediaChunkIterator.EMPTY, MediaChunkIterator.EMPTY
};
private static final long TEST_CHUNK_DURATION_US = 2_000_000;
@Mock private BandwidthMeter mockBandwidthMeter;
private FakeClock fakeClock;
@ -58,33 +57,53 @@ public final class AdaptiveTrackSelectionTest {
public void setUp() {
initMocks(this);
fakeClock = new FakeClock(0);
when(mockBandwidthMeter.getTimeToFirstByteEstimateUs()).thenReturn(C.TIME_UNSET);
}
@Test
public void selectInitialIndexUseMaxInitialBitrateIfNoBandwidthEstimate() {
public void initial_updateSelectedTrack_selectsHighestBitrateWithinBandwidth() {
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
Format format3 = videoFormat(/* bitrate= */ 2000, /* width= */ 960, /* height= */ 720);
TrackGroup trackGroup = new TrackGroup(format1, format2, format3);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L);
AdaptiveTrackSelection adaptiveTrackSelection = adaptiveTrackSelection(trackGroup);
AdaptiveTrackSelection adaptiveTrackSelection =
prepareAdaptiveTrackSelectionWithBandwidthFraction(trackGroup, /* bandwidthFraction= */ 1f);
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_INITIAL);
}
@Test
public void selectInitialIndexUseBandwidthEstimateIfAvailable() {
public void initial_updateSelectedTrack_selectsHighestBitrateWithinBandwidthFraction() {
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
Format format3 = videoFormat(/* bitrate= */ 2000, /* width= */ 960, /* height= */ 720);
TrackGroup trackGroup = new TrackGroup(format1, format2, format3);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(500L);
AdaptiveTrackSelection adaptiveTrackSelection = adaptiveTrackSelection(trackGroup);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(2000L);
AdaptiveTrackSelection adaptiveTrackSelection =
prepareAdaptiveTrackSelectionWithBandwidthFraction(
trackGroup, /* bandwidthFraction= */ 0.5f);
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format1);
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_INITIAL);
}
@Test
public void initial_updateSelectedTrack_selectsHighestBitrateWithinBandwidthAndTimeToFirstByte() {
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
Format format3 = videoFormat(/* bitrate= */ 2000, /* width= */ 960, /* height= */ 720);
TrackGroup trackGroup = new TrackGroup(format1, format2, format3);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(2000L);
when(mockBandwidthMeter.getTimeToFirstByteEstimateUs()).thenReturn(1_000_000L);
AdaptiveTrackSelection adaptiveTrackSelection =
prepareAdaptiveTrackSelectionWithBandwidthFraction(trackGroup, /* bandwidthFraction= */ 1f);
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_INITIAL);
}
@ -99,7 +118,7 @@ public final class AdaptiveTrackSelectionTest {
// if possible.
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L, 2000L);
AdaptiveTrackSelection adaptiveTrackSelection =
adaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
prepareAdaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
trackGroup, /* minDurationForQualityIncreaseMs= */ 10_000);
adaptiveTrackSelection.updateSelectedTrack(
@ -107,7 +126,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 9_999_000,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ Collections.emptyList(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
// When bandwidth estimation is updated to 2000L, we can switch up to use a higher bitrate
// format. However, since we only buffered 9_999_000 us, which is smaller than
@ -127,7 +146,7 @@ public final class AdaptiveTrackSelectionTest {
// if possible.
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L, 2000L);
AdaptiveTrackSelection adaptiveTrackSelection =
adaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
prepareAdaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
trackGroup, /* minDurationForQualityIncreaseMs= */ 10_000);
adaptiveTrackSelection.updateSelectedTrack(
@ -135,7 +154,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 10_000_000,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ Collections.emptyList(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
// When bandwidth estimation is updated to 2000L, we can switch up to use a higher bitrate
// format. When we have buffered enough (10_000_000 us, which is equal to
@ -155,7 +174,7 @@ public final class AdaptiveTrackSelectionTest {
// if necessary.
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L, 500L);
AdaptiveTrackSelection adaptiveTrackSelection =
adaptiveTrackSelectionWithMaxDurationForQualityDecreaseMs(
prepareAdaptiveTrackSelectionWithMaxDurationForQualityDecreaseMs(
trackGroup, /* maxDurationForQualityDecreaseMs= */ 25_000);
adaptiveTrackSelection.updateSelectedTrack(
@ -163,7 +182,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 25_000_000,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ Collections.emptyList(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
// When bandwidth estimation is updated to 500L, we should switch down to use a lower bitrate
// format. However, since we have enough buffer at higher quality (25_000_000 us, which is equal
@ -183,7 +202,7 @@ public final class AdaptiveTrackSelectionTest {
// if necessary.
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L, 500L);
AdaptiveTrackSelection adaptiveTrackSelection =
adaptiveTrackSelectionWithMaxDurationForQualityDecreaseMs(
prepareAdaptiveTrackSelectionWithMaxDurationForQualityDecreaseMs(
trackGroup, /* maxDurationForQualityDecreaseMs= */ 25_000);
adaptiveTrackSelection.updateSelectedTrack(
@ -191,7 +210,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 24_999_000,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ Collections.emptyList(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
// When bandwidth estimation is updated to 500L, we should switch down to use a lower bitrate
// format. When we don't have enough buffer at higher quality (24_999_000 us is smaller than
@ -219,7 +238,7 @@ public final class AdaptiveTrackSelectionTest {
queue.add(chunk3);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(500L);
AdaptiveTrackSelection adaptiveTrackSelection = adaptiveTrackSelection(trackGroup);
AdaptiveTrackSelection adaptiveTrackSelection = prepareAdaptiveTrackSelection(trackGroup);
int size = adaptiveTrackSelection.evaluateQueueSize(0, queue);
assertThat(size).isEqualTo(3);
@ -245,7 +264,7 @@ public final class AdaptiveTrackSelectionTest {
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(500L);
AdaptiveTrackSelection adaptiveTrackSelection =
adaptiveTrackSelectionWithMinTimeBetweenBufferReevaluationMs(
prepareAdaptiveTrackSelectionWithMinTimeBetweenBufferReevaluationMs(
trackGroup, /* durationToRetainAfterDiscardMs= */ 15_000);
int initialQueueSize = adaptiveTrackSelection.evaluateQueueSize(0, queue);
@ -285,7 +304,7 @@ public final class AdaptiveTrackSelectionTest {
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(500L);
AdaptiveTrackSelection adaptiveTrackSelection =
adaptiveTrackSelectionWithMinTimeBetweenBufferReevaluationMs(
prepareAdaptiveTrackSelectionWithMinTimeBetweenBufferReevaluationMs(
trackGroup, /* durationToRetainAfterDiscardMs= */ 15_000);
int initialQueueSize = adaptiveTrackSelection.evaluateQueueSize(0, queue);
@ -344,7 +363,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 4_000_000,
/* availableDurationUs= */ C.TIME_UNSET,
queue,
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format1);
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_ADAPTIVE);
@ -358,7 +377,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 4_000_000,
/* availableDurationUs= */ C.TIME_UNSET,
queue,
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_INITIAL);
@ -370,7 +389,7 @@ public final class AdaptiveTrackSelectionTest {
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
TrackGroup trackGroup = new TrackGroup(format1, format2);
AdaptiveTrackSelection adaptiveTrackSelection =
prepareTrackSelection(adaptiveTrackSelection(trackGroup));
prepareTrackSelection(prepareAdaptiveTrackSelection(trackGroup));
Format unknownFormat = videoFormat(/* bitrate= */ 42, /* width= */ 300, /* height= */ 123);
FakeMediaChunk chunk =
new FakeMediaChunk(unknownFormat, /* startTimeUs= */ 0, /* endTimeUs= */ 2_000_000);
@ -381,7 +400,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 2_000_000,
/* availableDurationUs= */ C.TIME_UNSET,
queue,
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
assertThat(adaptiveTrackSelection.getSelectedFormat()).isAnyOf(format1, format2);
}
@ -404,7 +423,7 @@ public final class AdaptiveTrackSelectionTest {
new AdaptationCheckpoint(/* totalBandwidth= */ 5000, /* allocatedBandwidth= */ 1300));
AdaptiveTrackSelection adaptiveTrackSelection =
prepareTrackSelection(
adaptiveTrackSelectionWithAdaptationCheckpoints(trackGroup, checkpoints));
prepareAdaptiveTrackSelectionWithAdaptationCheckpoints(trackGroup, checkpoints));
// Ensure format0 is selected initially so that we can assert the upswitches.
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1L);
@ -413,7 +432,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 999_999_999_999L,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ ImmutableList.of(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format0);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(999L);
@ -422,7 +441,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 999_999_999_999L,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ ImmutableList.of(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format0);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L);
@ -431,7 +450,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 999_999_999_999L,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ ImmutableList.of(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format1);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(2499L);
@ -440,7 +459,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 999_999_999_999L,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ ImmutableList.of(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format1);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(3500L);
@ -449,7 +468,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 999_999_999_999L,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ ImmutableList.of(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(8999L);
@ -458,7 +477,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 999_999_999_999L,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ ImmutableList.of(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(9000L);
@ -467,7 +486,7 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 999_999_999_999L,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ ImmutableList.of(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format3);
}
@ -581,12 +600,29 @@ public final class AdaptiveTrackSelectionTest {
.inOrder();
}
private AdaptiveTrackSelection adaptiveTrackSelection(TrackGroup trackGroup) {
return adaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
private AdaptiveTrackSelection prepareAdaptiveTrackSelection(TrackGroup trackGroup) {
return prepareAdaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
trackGroup, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS);
}
private AdaptiveTrackSelection adaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
private AdaptiveTrackSelection prepareAdaptiveTrackSelectionWithBandwidthFraction(
TrackGroup trackGroup, float bandwidthFraction) {
return prepareTrackSelection(
new AdaptiveTrackSelection(
trackGroup,
selectedAllTracksInGroup(trackGroup),
TrackSelection.TYPE_UNSET,
mockBandwidthMeter,
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS,
AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
bandwidthFraction,
AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE,
/* adaptationCheckpoints= */ ImmutableList.of(),
fakeClock));
}
private AdaptiveTrackSelection prepareAdaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
TrackGroup trackGroup, long minDurationForQualityIncreaseMs) {
return prepareTrackSelection(
new AdaptiveTrackSelection(
@ -603,7 +639,7 @@ public final class AdaptiveTrackSelectionTest {
fakeClock));
}
private AdaptiveTrackSelection adaptiveTrackSelectionWithMaxDurationForQualityDecreaseMs(
private AdaptiveTrackSelection prepareAdaptiveTrackSelectionWithMaxDurationForQualityDecreaseMs(
TrackGroup trackGroup, long maxDurationForQualityDecreaseMs) {
return prepareTrackSelection(
new AdaptiveTrackSelection(
@ -620,8 +656,9 @@ public final class AdaptiveTrackSelectionTest {
fakeClock));
}
private AdaptiveTrackSelection adaptiveTrackSelectionWithMinTimeBetweenBufferReevaluationMs(
TrackGroup trackGroup, long durationToRetainAfterDiscardMs) {
private AdaptiveTrackSelection
prepareAdaptiveTrackSelectionWithMinTimeBetweenBufferReevaluationMs(
TrackGroup trackGroup, long durationToRetainAfterDiscardMs) {
return prepareTrackSelection(
new AdaptiveTrackSelection(
trackGroup,
@ -637,7 +674,7 @@ public final class AdaptiveTrackSelectionTest {
fakeClock));
}
private AdaptiveTrackSelection adaptiveTrackSelectionWithAdaptationCheckpoints(
private AdaptiveTrackSelection prepareAdaptiveTrackSelectionWithAdaptationCheckpoints(
TrackGroup trackGroup, List<AdaptationCheckpoint> adaptationCheckpoints) {
return prepareTrackSelection(
new AdaptiveTrackSelection(
@ -662,10 +699,35 @@ public final class AdaptiveTrackSelectionTest {
/* bufferedDurationUs= */ 0,
/* availableDurationUs= */ C.TIME_UNSET,
/* queue= */ Collections.emptyList(),
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
createMediaChunkIterators(adaptiveTrackSelection.getTrackGroup(), TEST_CHUNK_DURATION_US));
return adaptiveTrackSelection;
}
private MediaChunkIterator[] createMediaChunkIterators(
TrackGroup trackGroup, long chunkDurationUs) {
MediaChunkIterator[] iterators = new MediaChunkIterator[trackGroup.length];
for (int i = 0; i < trackGroup.length; i++) {
iterators[i] =
new BaseMediaChunkIterator(/* fromIndex= */ 0, /* toIndex= */ 0) {
@Override
public DataSpec getDataSpec() {
return new DataSpec.Builder().setUri("https://test.example").build();
}
@Override
public long getChunkStartTimeUs() {
return 123_456_789;
}
@Override
public long getChunkEndTimeUs() {
return 123_456_789 + chunkDurationUs;
}
};
}
return iterators;
}
private int[] selectedAllTracksInGroup(TrackGroup trackGroup) {
int[] listIndices = new int[trackGroup.length];
for (int i = 0; i < trackGroup.length; i++) {