diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java index 2358746d65..e02f8198d5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java @@ -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 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 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 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. * diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java index 98d0f36ff7..021694eb23 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java @@ -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 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++) {