mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
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:
parent
57796914a7
commit
3e613beb78
2 changed files with 151 additions and 50 deletions
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue