AdaptiveTrackSelection extends BaseTrackSelection { @Override public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) { long nowMs = clock.elapsedRealtime(); if (!shouldEvaluateQueueSize(nowMs)) { return queue.size(); } lastBufferEvaluationMs = nowMs; if (queue.isEmpty()) { return 0; } int queueSize = queue.size(); MediaChunk lastChunk = queue.get(queueSize - 1); long playoutBufferedDurationBeforeLastChunkUs = Util.getPlayoutDurationForMediaDuration( lastChunk.startTimeUs - playbackPositionUs, playbackSpeed); long minDurationToRetainAfterDiscardUs = getMinDurationToRetainAfterDiscardUs(); if (playoutBufferedDurationBeforeLastChunkUs < minDurationToRetainAfterDiscardUs) { return queueSize; } int idealSelectedIndex = determineIdealSelectedIndex(nowMs); Format idealFormat = getFormat(idealSelectedIndex); for (int i = 0; i < queueSize; i++) { MediaChunk chunk = queue.get(i); Format format = chunk.trackFormat; long mediaDurationBeforeThisChunkUs = chunk.startTimeUs - playbackPositionUs; long playoutDurationBeforeThisChunkUs = Util.getPlayoutDurationForMediaDuration(mediaDurationBeforeThisChunkUs, playbackSpeed); if (playoutDurationBeforeThisChunkUs >= minDurationToRetainAfterDiscardUs && format.bitrate < idealFormat.bitrate && format.height != Format.NO_VALUE && format.height < 720 && format.width != Format.NO_VALUE && format.width < 1280 && format.height < idealFormat.height) { return i; } } return queueSize; } AdaptiveTrackSelection(TrackGroup group, int[] tracks,
      BandwidthMeter bandwidthMeter);  AdaptiveTrackSelection(
      TrackGroup group,
      int[] tracks,
      BandwidthMeter bandwidthMeter,
      long reservedBandwidth,
      long minDurationForQualityIncreaseMs,
      long maxDurationForQualityDecreaseMs,
      long minDurationToRetainAfterDiscardMs,
      float bandwidthFraction,
      float bufferedFractionToLiveEdgeForQualityIncrease,
      long minTimeBetweenBufferReevaluationMs,
      Clock clock); private  AdaptiveTrackSelection(
      TrackGroup group,
      int[] tracks,
      BandwidthProvider bandwidthProvider,
      long minDurationForQualityIncreaseMs,
      long maxDurationForQualityDecreaseMs,
      long minDurationToRetainAfterDiscardMs,
      float bufferedFractionToLiveEdgeForQualityIncrease,
      long minTimeBetweenBufferReevaluationMs,
      Clock clock); void experimental_setBandwidthAllocationCheckpoints(long[][] allocationCheckpoints); @Override void enable(); @Override void onPlaybackSpeed(float playbackSpeed); @Override void updateSelectedTrack(
      long playbackPositionUs,
      long bufferedDurationUs,
      long availableDurationUs,
      List<? extends MediaChunk> queue,
      MediaChunkIterator[] mediaChunkIterators); @Override int getSelectedIndex(); @Override int getSelectionReason(); @Override @Nullable Object getSelectionData(); @Override int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue); static final int DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS; static final int DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS; static final int DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS; static final float DEFAULT_BANDWIDTH_FRACTION; static final float DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE; static final long DEFAULT_MIN_TIME_BETWEEN_BUFFER_REEVALUTATION_MS; }
@Test public void testEvaluateQueueSizeDoNotReevaluateUntilAfterMinTimeBetweenBufferReevaluation() { Format format1 = videoFormat( 500, 320, 240); Format format2 = videoFormat( 1000, 640, 480); Format format3 = videoFormat( 2000, 960, 720); TrackGroup trackGroup = new TrackGroup(format1, format2, format3); FakeMediaChunk chunk1 = new FakeMediaChunk(format1, 0, 10_000_000); FakeMediaChunk chunk2 = new FakeMediaChunk(format1, 10_000_000, 20_000_000); FakeMediaChunk chunk3 = new FakeMediaChunk(format1, 20_000_000, 30_000_000); List<FakeMediaChunk> queue = new ArrayList<>(); queue.add(chunk1); queue.add(chunk2); queue.add(chunk3); when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(500L); adaptiveTrackSelection = adaptiveTrackSelectionWithMinTimeBetweenBufferReevaluationMs( trackGroup, 15_000, 2000); int initialQueueSize = adaptiveTrackSelection.evaluateQueueSize(0, queue); fakeClock.advanceTime(1999); when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L); int newSize = adaptiveTrackSelection.evaluateQueueSize(0, queue); assertThat(newSize).isEqualTo(initialQueueSize); }
@Test public void testEvaluateQueueSizeRetainMoreThanMinimumDurationAfterDiscard() { Format format1 = videoFormat( 500, 320, 240); Format format2 = videoFormat( 1000, 640, 480); Format format3 = videoFormat( 2000, 960, 720); TrackGroup trackGroup = new TrackGroup(format1, format2, format3); FakeMediaChunk chunk1 = new FakeMediaChunk(format1, 0, 10_000_000); FakeMediaChunk chunk2 = new FakeMediaChunk(format1, 10_000_000, 20_000_000); FakeMediaChunk chunk3 = new FakeMediaChunk(format1, 20_000_000, 30_000_000); List<FakeMediaChunk> queue = new ArrayList<>(); queue.add(chunk1); queue.add(chunk2); queue.add(chunk3); when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(500L); adaptiveTrackSelection = adaptiveTrackSelectionWithMinTimeBetweenBufferReevaluationMs( trackGroup, 15_000, 2000); int initialQueueSize = adaptiveTrackSelection.evaluateQueueSize(0, queue); assertThat(initialQueueSize).isEqualTo(3); fakeClock.advanceTime(2000); when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L); int newSize = adaptiveTrackSelection.evaluateQueueSize(0, queue); assertThat(newSize).isEqualTo(2); }