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 f457701031..fc3783d56b 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 @@ -47,8 +47,6 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { private final long minTimeBetweenBufferReevaluationMs; private final Clock clock; - private TrackBitrateEstimator trackBitrateEstimator; - /** Creates an adaptive track selection factory with default parameters. */ public Factory() { this( @@ -202,19 +200,6 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { bufferedFractionToLiveEdgeForQualityIncrease; this.minTimeBetweenBufferReevaluationMs = minTimeBetweenBufferReevaluationMs; this.clock = clock; - trackBitrateEstimator = TrackBitrateEstimator.DEFAULT; - } - - /** - * Sets a TrackBitrateEstimator. - * - *

This method is experimental, and will be renamed or removed in a future release. - * - * @param trackBitrateEstimator A {@link TrackBitrateEstimator}. - */ - public final void experimental_setTrackBitrateEstimator( - TrackBitrateEstimator trackBitrateEstimator) { - this.trackBitrateEstimator = trackBitrateEstimator; } @Override @@ -245,7 +230,6 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { AdaptiveTrackSelection adaptiveSelection = createAdaptiveTrackSelection( definition.group, bandwidthMeter, definition.tracks, totalFixedBandwidth); - adaptiveSelection.experimental_setTrackBitrateEstimator(trackBitrateEstimator); adaptiveSelections.add(adaptiveSelection); selections[i] = adaptiveSelection; } @@ -312,11 +296,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { private final float bufferedFractionToLiveEdgeForQualityIncrease; private final long minTimeBetweenBufferReevaluationMs; private final Clock clock; - private final Format[] formats; - private final int[] formatBitrates; - private final int[] trackBitrates; - private TrackBitrateEstimator trackBitrateEstimator; private float playbackSpeed; private int selectedIndex; private int reason; @@ -419,27 +399,6 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { playbackSpeed = 1f; reason = C.SELECTION_REASON_UNKNOWN; lastBufferEvaluationMs = C.TIME_UNSET; - trackBitrateEstimator = TrackBitrateEstimator.DEFAULT; - formats = new Format[length]; - formatBitrates = new int[length]; - trackBitrates = new int[length]; - for (int i = 0; i < length; i++) { - @SuppressWarnings("nullness:method.invocation.invalid") - Format format = getFormat(i); - formats[i] = format; - formatBitrates[i] = formats[i].bitrate; - } - } - - /** - * Sets a TrackBitrateEstimator. - * - *

This method is experimental, and will be renamed or removed in a future release. - * - * @param trackBitrateEstimator A {@link TrackBitrateEstimator}. - */ - public void experimental_setTrackBitrateEstimator(TrackBitrateEstimator trackBitrateEstimator) { - this.trackBitrateEstimator = trackBitrateEstimator; } /** @@ -472,19 +431,16 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { MediaChunkIterator[] mediaChunkIterators) { long nowMs = clock.elapsedRealtime(); - // Update the estimated track bitrates. - trackBitrateEstimator.getBitrates(formats, queue, mediaChunkIterators, trackBitrates); - // Make initial selection if (reason == C.SELECTION_REASON_UNKNOWN) { reason = C.SELECTION_REASON_INITIAL; - selectedIndex = determineIdealSelectedIndex(nowMs, trackBitrates); + selectedIndex = determineIdealSelectedIndex(nowMs); return; } // Stash the current selection, then make a new one. int currentSelectedIndex = selectedIndex; - selectedIndex = determineIdealSelectedIndex(nowMs, trackBitrates); + selectedIndex = determineIdealSelectedIndex(nowMs); if (selectedIndex == currentSelectedIndex) { return; } @@ -548,7 +504,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { if (playoutBufferedDurationBeforeLastChunkUs < minDurationToRetainAfterDiscardUs) { return queueSize; } - int idealSelectedIndex = determineIdealSelectedIndex(nowMs, formatBitrates); + int idealSelectedIndex = determineIdealSelectedIndex(nowMs); 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 @@ -613,16 +569,14 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { * * @param nowMs The current time in the timebase of {@link Clock#elapsedRealtime()}, or {@link * Long#MIN_VALUE} to ignore blacklisting. - * @param trackBitrates The estimated track bitrates. May differ from format bitrates if more - * accurate estimates of the current track bitrates are available. */ - private int determineIdealSelectedIndex(long nowMs, int[] trackBitrates) { + private int determineIdealSelectedIndex(long nowMs) { long effectiveBitrate = bandwidthProvider.getAllocatedBandwidth(); int lowestBitrateNonBlacklistedIndex = 0; for (int i = 0; i < length; i++) { if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) { Format format = getFormat(i); - if (canSelectFormat(format, trackBitrates[i], playbackSpeed, effectiveBitrate)) { + if (canSelectFormat(format, format.bitrate, playbackSpeed, effectiveBitrate)) { return i; } else { lowestBitrateNonBlacklistedIndex = i; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackBitrateEstimator.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackBitrateEstimator.java deleted file mode 100644 index 1cd6c09bfe..0000000000 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackBitrateEstimator.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.exoplayer2.trackselection; - -import androidx.annotation.Nullable; -import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.source.chunk.MediaChunk; -import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; -import java.util.List; - -/** Estimates track bitrate values. */ -public interface TrackBitrateEstimator { - - /** - * A {@link TrackBitrateEstimator} that returns the bitrate values defined in the track formats. - */ - TrackBitrateEstimator DEFAULT = - (formats, queue, iterators, bitrates) -> - TrackSelectionUtil.getFormatBitrates(formats, bitrates); - - /** - * Returns bitrate values for a set of tracks whose formats are given. - * - * @param formats The track formats. - * @param queue The queue of already buffered {@link MediaChunk} instances. Must not be modified. - * @param iterators An array of {@link MediaChunkIterator}s providing information about the - * sequence of upcoming media chunks for each track. - * @param bitrates An array into which the bitrate values will be written. If non-null, this array - * is the one that will be returned. - * @return Bitrate values for the tracks. As long as the format of a track has set bitrate, a - * bitrate value is set in the returned array. Otherwise it might be set to {@link - * Format#NO_VALUE}. - */ - int[] getBitrates( - Format[] formats, - List queue, - MediaChunkIterator[] iterators, - @Nullable int[] bitrates); -} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java index 71afd87b0f..0f2748b1ac 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java @@ -16,18 +16,9 @@ package com.google.android.exoplayer2.trackselection; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.source.chunk.MediaChunk; -import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; -import com.google.android.exoplayer2.source.chunk.MediaChunkListIterator; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; import com.google.android.exoplayer2.trackselection.TrackSelection.Definition; -import com.google.android.exoplayer2.util.Assertions; -import java.util.Arrays; -import java.util.List; import org.checkerframework.checker.nullness.compatqual.NullableType; /** Track selection related utility methods. */ @@ -106,261 +97,4 @@ public final class TrackSelectionUtil { } return builder.build(); } - - /** - * Returns average bitrate for chunks in bits per second. Chunks are included in average until - * {@code maxDurationMs} or the first unknown length chunk. - * - * @param iterator Iterator for media chunk sequences. - * @param maxDurationUs Maximum duration of chunks to be included in average bitrate, in - * microseconds. - * @return Average bitrate for chunks in bits per second, or {@link Format#NO_VALUE} if there are - * no chunks or the first chunk length is unknown. - */ - public static int getAverageBitrate(MediaChunkIterator iterator, long maxDurationUs) { - long totalDurationUs = 0; - long totalLength = 0; - while (iterator.next()) { - long chunkLength = iterator.getDataSpec().length; - if (chunkLength == C.LENGTH_UNSET) { - break; - } - long chunkDurationUs = iterator.getChunkEndTimeUs() - iterator.getChunkStartTimeUs(); - if (totalDurationUs + chunkDurationUs >= maxDurationUs) { - totalLength += chunkLength * (maxDurationUs - totalDurationUs) / chunkDurationUs; - totalDurationUs = maxDurationUs; - break; - } - totalDurationUs += chunkDurationUs; - totalLength += chunkLength; - } - return totalDurationUs == 0 - ? Format.NO_VALUE - : (int) (totalLength * C.BITS_PER_BYTE * C.MICROS_PER_SECOND / totalDurationUs); - } - - /** - * Returns bitrate values for a set of tracks whose upcoming media chunk iterators and formats are - * given. - * - *

If an average bitrate can't be calculated, an estimation is calculated using average bitrate - * of another track and the ratio of the bitrate values defined in the formats of the two tracks. - * - * @param iterators An array of {@link MediaChunkIterator}s providing information about the - * sequence of upcoming media chunks for each track. - * @param formats The track formats. - * @param maxDurationUs Maximum duration of chunks to be included in average bitrate values, in - * microseconds. - * @param bitrates If not null, stores bitrate values in this array. - * @return Average bitrate values for the tracks. If for a track, an average bitrate or an - * estimation can't be calculated, {@link Format#NO_VALUE} is set. - * @see #getAverageBitrate(MediaChunkIterator, long) - */ - @VisibleForTesting - /* package */ static int[] getBitratesUsingFutureInfo( - MediaChunkIterator[] iterators, - Format[] formats, - long maxDurationUs, - @Nullable int[] bitrates) { - int trackCount = iterators.length; - Assertions.checkArgument(trackCount == formats.length); - if (trackCount == 0) { - return new int[0]; - } - if (bitrates == null) { - bitrates = new int[trackCount]; - } - if (maxDurationUs == 0) { - Arrays.fill(bitrates, Format.NO_VALUE); - return bitrates; - } - - int[] formatBitrates = new int[trackCount]; - float[] bitrateRatios = new float[trackCount]; - boolean needEstimateBitrate = false; - boolean canEstimateBitrate = false; - for (int i = 0; i < trackCount; i++) { - int bitrate = getAverageBitrate(iterators[i], maxDurationUs); - if (bitrate != Format.NO_VALUE) { - int formatBitrate = formats[i].bitrate; - formatBitrates[i] = formatBitrate; - if (formatBitrate != Format.NO_VALUE) { - bitrateRatios[i] = ((float) bitrate) / formatBitrate; - canEstimateBitrate = true; - } - } else { - needEstimateBitrate = true; - formatBitrates[i] = Format.NO_VALUE; - } - bitrates[i] = bitrate; - } - - if (needEstimateBitrate && canEstimateBitrate) { - estimateBitrates(bitrates, formats, formatBitrates, bitrateRatios); - } - return bitrates; - } - - /** - * Returns bitrate values for a set of tracks whose formats are given, using the given queue of - * already buffered {@link MediaChunk} instances. - * - * @param queue The queue of already buffered {@link MediaChunk} instances. Must not be modified. - * @param formats The track formats. - * @param maxDurationUs Maximum duration of chunks to be included in average bitrate values, in - * microseconds. - * @param bitrates If not null, calculates bitrate values only for indexes set to Format.NO_VALUE - * and stores result in this array. - * @return Bitrate values for the tracks. If for a track, a bitrate value can't be calculated, - * {@link Format#NO_VALUE} is set. - * @see #getBitratesUsingFutureInfo(MediaChunkIterator[], Format[], long, int[]) - */ - @VisibleForTesting - /* package */ static int[] getBitratesUsingPastInfo( - List queue, - Format[] formats, - long maxDurationUs, - @Nullable int[] bitrates) { - if (bitrates == null) { - bitrates = new int[formats.length]; - Arrays.fill(bitrates, Format.NO_VALUE); - } - if (maxDurationUs == 0) { - return bitrates; - } - int queueAverageBitrate = getAverageQueueBitrate(queue, maxDurationUs); - if (queueAverageBitrate == Format.NO_VALUE) { - return bitrates; - } - int queueFormatBitrate = queue.get(queue.size() - 1).trackFormat.bitrate; - if (queueFormatBitrate != Format.NO_VALUE) { - float queueBitrateRatio = ((float) queueAverageBitrate) / queueFormatBitrate; - estimateBitrates( - bitrates, formats, new int[] {queueFormatBitrate}, new float[] {queueBitrateRatio}); - } - return bitrates; - } - - /** - * Returns bitrate values for a set of tracks whose formats are given, using the given upcoming - * media chunk iterators and the queue of already buffered {@link MediaChunk}s. - * - * @param formats The track formats. - * @param queue The queue of already buffered {@link MediaChunk}s. Must not be modified. - * @param maxPastDurationUs Maximum duration of past chunks to be included in average bitrate - * values, in microseconds. - * @param iterators An array of {@link MediaChunkIterator}s providing information about the - * sequence of upcoming media chunks for each track. - * @param maxFutureDurationUs Maximum duration of future chunks to be included in average bitrate - * values, in microseconds. - * @param useFormatBitrateAsLowerBound Whether to return the estimated bitrate only if it's higher - * than the bitrate of the track's format. - * @param bitrates An array into which the bitrate values will be written. If non-null, this array - * is the one that will be returned. - * @return Bitrate values for the tracks. As long as the format of a track has set bitrate, a - * bitrate value is set in the returned array. Otherwise it might be set to {@link - * Format#NO_VALUE}. - */ - public static int[] getBitratesUsingPastAndFutureInfo( - Format[] formats, - List queue, - long maxPastDurationUs, - MediaChunkIterator[] iterators, - long maxFutureDurationUs, - boolean useFormatBitrateAsLowerBound, - @Nullable int[] bitrates) { - bitrates = getBitratesUsingFutureInfo(iterators, formats, maxFutureDurationUs, bitrates); - getBitratesUsingPastInfo(queue, formats, maxPastDurationUs, bitrates); - for (int i = 0; i < bitrates.length; i++) { - int bitrate = bitrates[i]; - if (bitrate == Format.NO_VALUE - || (useFormatBitrateAsLowerBound - && formats[i].bitrate != Format.NO_VALUE - && bitrate < formats[i].bitrate)) { - bitrates[i] = formats[i].bitrate; - } - } - return bitrates; - } - - /** - * Returns an array containing {@link Format#bitrate} values for given each format in order. - * - * @param formats The format array to copy {@link Format#bitrate} values. - * @param bitrates If not null, stores bitrate values in this array. - * @return An array containing {@link Format#bitrate} values for given each format in order. - */ - public static int[] getFormatBitrates(Format[] formats, @Nullable int[] bitrates) { - int trackCount = formats.length; - if (bitrates == null) { - bitrates = new int[trackCount]; - } - for (int i = 0; i < trackCount; i++) { - bitrates[i] = formats[i].bitrate; - } - return bitrates; - } - - /** - * Fills missing values in the given {@code bitrates} array by calculates an estimation using the - * closest reference bitrate value. - * - * @param bitrates An array of bitrates to be filled with estimations. Missing values are set to - * {@link Format#NO_VALUE}. - * @param formats An array of formats, one for each bitrate. - * @param referenceBitrates An array of reference bitrates which are used to calculate - * estimations. - * @param referenceBitrateRatios An array containing ratio of reference bitrates to their bitrate - * estimates. - */ - private static void estimateBitrates( - int[] bitrates, Format[] formats, int[] referenceBitrates, float[] referenceBitrateRatios) { - for (int i = 0; i < bitrates.length; i++) { - if (bitrates[i] == Format.NO_VALUE) { - int formatBitrate = formats[i].bitrate; - if (formatBitrate != Format.NO_VALUE) { - int closestReferenceBitrateIndex = - getClosestBitrateIndex(formatBitrate, referenceBitrates); - bitrates[i] = - (int) (referenceBitrateRatios[closestReferenceBitrateIndex] * formatBitrate); - } - } - } - } - - private static int getAverageQueueBitrate(List queue, long maxDurationUs) { - if (queue.isEmpty()) { - return Format.NO_VALUE; - } - MediaChunkListIterator iterator = - new MediaChunkListIterator(getSingleFormatSubQueue(queue), /* reverseOrder= */ true); - return getAverageBitrate(iterator, maxDurationUs); - } - - private static List getSingleFormatSubQueue( - List queue) { - Format queueFormat = queue.get(queue.size() - 1).trackFormat; - int queueSize = queue.size(); - for (int i = queueSize - 2; i >= 0; i--) { - if (!queue.get(i).trackFormat.equals(queueFormat)) { - return queue.subList(i + 1, queueSize); - } - } - return queue; - } - - private static int getClosestBitrateIndex(int formatBitrate, int[] formatBitrates) { - int closestDistance = Integer.MAX_VALUE; - int closestFormat = C.INDEX_UNSET; - for (int j = 0; j < formatBitrates.length; j++) { - if (formatBitrates[j] != Format.NO_VALUE) { - int distance = Math.abs(formatBitrates[j] - formatBitrate); - if (distance < closestDistance) { - closestDistance = distance; - closestFormat = j; - } - } - } - return closestFormat; - } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/WindowedTrackBitrateEstimator.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/WindowedTrackBitrateEstimator.java deleted file mode 100644 index 25f7e4ea73..0000000000 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/WindowedTrackBitrateEstimator.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.exoplayer2.trackselection; - -import androidx.annotation.Nullable; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.source.chunk.MediaChunk; -import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; -import java.util.List; - -/** A {@link TrackBitrateEstimator} which derives estimates from a window of time. */ -public final class WindowedTrackBitrateEstimator implements TrackBitrateEstimator { - - private final long maxPastDurationUs; - private final long maxFutureDurationUs; - private final boolean useFormatBitrateAsLowerBound; - - /** - * @param maxPastDurationMs Maximum duration of past chunks to be included in average bitrate - * values, in milliseconds. - * @param maxFutureDurationMs Maximum duration of future chunks to be included in average bitrate - * values, in milliseconds. - * @param useFormatBitrateAsLowerBound Whether to use the bitrate of the track's format as a lower - * bound for the estimated bitrate. - */ - public WindowedTrackBitrateEstimator( - long maxPastDurationMs, long maxFutureDurationMs, boolean useFormatBitrateAsLowerBound) { - this.maxPastDurationUs = C.msToUs(maxPastDurationMs); - this.maxFutureDurationUs = C.msToUs(maxFutureDurationMs); - this.useFormatBitrateAsLowerBound = useFormatBitrateAsLowerBound; - } - - @Override - public int[] getBitrates( - Format[] formats, - List queue, - MediaChunkIterator[] iterators, - @Nullable int[] bitrates) { - if (maxFutureDurationUs > 0 || maxPastDurationUs > 0) { - return TrackSelectionUtil.getBitratesUsingPastAndFutureInfo( - formats, - queue, - maxPastDurationUs, - iterators, - maxFutureDurationUs, - useFormatBitrateAsLowerBound, - bitrates); - } - return TrackSelectionUtil.getFormatBitrates(formats, bitrates); - } -} 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 456f7f7107..af935048e8 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 @@ -16,9 +16,6 @@ package com.google.android.exoplayer2.trackselection; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -37,13 +34,11 @@ import com.google.android.exoplayer2.trackselection.TrackSelection.Definition; import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.util.MimeTypes; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentMatcher; import org.mockito.Mock; /** Unit test for {@link AdaptiveTrackSelection}. */ @@ -231,54 +226,6 @@ public final class AdaptiveTrackSelectionTest { assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_ADAPTIVE); } - @Test - public void testUpdateSelectedTrackSwitchUpIfTrackBitrateEstimateIsLow() { - 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); - - // The second measurement onward returns 1500L, which isn't enough to switch up to format3 as - // the format bitrate is 2000. - when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L, 1500L); - - // But TrackBitrateEstimator returns 1500 for 3rd track so it should switch up. - TrackBitrateEstimator estimator = mock(TrackBitrateEstimator.class); - when(estimator.getBitrates(any(), any(), any(), any())) - .then( - (invocation) -> { - int[] returnValue = new int[] {500, 1000, 1500}; - int[] inputArray = (int[]) invocation.getArguments()[3]; - System.arraycopy(returnValue, 0, inputArray, 0, returnValue.length); - return returnValue; - }); - - adaptiveTrackSelection = adaptiveTrackSelection(trackGroup); - adaptiveTrackSelection.experimental_setTrackBitrateEstimator(estimator); - - adaptiveTrackSelection.updateSelectedTrack( - /* playbackPositionUs= */ 0, - /* bufferedDurationUs= */ AdaptiveTrackSelection - .DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS - * 1000, - /* availableDurationUs= */ C.TIME_UNSET, - /* queue= */ Collections.emptyList(), - /* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS); - - ArgumentMatcher matcher = - formats -> - formats.length == 3 - && Arrays.asList(formats).containsAll(Arrays.asList(format1, format2, format3)); - verify(estimator) - .getBitrates( - argThat(matcher), - eq(Collections.emptyList()), - eq(THREE_EMPTY_MEDIA_CHUNK_ITERATORS), - any()); - assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format3); - assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_ADAPTIVE); - } - @Test public void testEvaluateQueueSizeReturnQueueSizeIfBandwidthIsNotImproved() { Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtilTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtilTest.java deleted file mode 100644 index 963e90f139..0000000000 --- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtilTest.java +++ /dev/null @@ -1,617 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.trackselection; - -import static com.google.common.truth.Truth.assertThat; - -import android.net.Uri; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; -import com.google.android.exoplayer2.testutil.FakeMediaChunk; -import com.google.android.exoplayer2.testutil.FakeMediaChunkIterator; -import com.google.android.exoplayer2.upstream.DataSpec; -import java.util.Arrays; -import java.util.Collections; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** {@link TrackSelectionUtil} tests. */ -@RunWith(AndroidJUnit4.class) -public class TrackSelectionUtilTest { - - public static final long MAX_DURATION_US = 30 * C.MICROS_PER_SECOND; - - @Test - public void getAverageBitrate_emptyIterator_returnsNoValue() { - assertThat(TrackSelectionUtil.getAverageBitrate(MediaChunkIterator.EMPTY, MAX_DURATION_US)) - .isEqualTo(Format.NO_VALUE); - } - - @Test - public void getAverageBitrate_oneChunk_returnsChunkBitrate() { - long[] chunkTimeBoundariesSec = {12, 17}; - long[] chunkLengths = {10}; - - FakeMediaChunkIterator iterator = - new FakeMediaChunkIterator(chunkTimeBoundariesSec, chunkLengths); - - assertThat(TrackSelectionUtil.getAverageBitrate(iterator, MAX_DURATION_US)).isEqualTo(16); - } - - @Test - public void getAverageBitrate_multipleSameDurationChunks_returnsAverageChunkBitrate() { - long[] chunkTimeBoundariesSec = {0, 5, 10}; - long[] chunkLengths = {10, 20}; - - FakeMediaChunkIterator iterator = - new FakeMediaChunkIterator(chunkTimeBoundariesSec, chunkLengths); - - assertThat(TrackSelectionUtil.getAverageBitrate(iterator, MAX_DURATION_US)).isEqualTo(24); - } - - @Test - public void getAverageBitrate_multipleDifferentDurationChunks_returnsAverageChunkBitrate() { - long[] chunkTimeBoundariesSec = {0, 5, 15, 30}; - long[] chunkLengths = {10, 20, 30}; - - FakeMediaChunkIterator iterator = - new FakeMediaChunkIterator(chunkTimeBoundariesSec, chunkLengths); - - assertThat(TrackSelectionUtil.getAverageBitrate(iterator, MAX_DURATION_US)).isEqualTo(16); - } - - @Test - public void getAverageBitrate_firstChunkLengthUnset_returnsNoValue() { - long[] chunkTimeBoundariesSec = {0, 5, 15, 30}; - long[] chunkLengths = {C.LENGTH_UNSET, 20, 30}; - - FakeMediaChunkIterator iterator = - new FakeMediaChunkIterator(chunkTimeBoundariesSec, chunkLengths); - - assertThat(TrackSelectionUtil.getAverageBitrate(iterator, MAX_DURATION_US)) - .isEqualTo(Format.NO_VALUE); - } - - @Test - public void getAverageBitrate_secondChunkLengthUnset_returnsFirstChunkBitrate() { - long[] chunkTimeBoundariesSec = {0, 5, 15, 30}; - long[] chunkLengths = {10, C.LENGTH_UNSET, 30}; - - FakeMediaChunkIterator iterator = - new FakeMediaChunkIterator(chunkTimeBoundariesSec, chunkLengths); - - assertThat(TrackSelectionUtil.getAverageBitrate(iterator, MAX_DURATION_US)).isEqualTo(16); - } - - @Test - public void - getAverageBitrate_chunksExceedingMaxDuration_returnsAverageChunkBitrateUpToMaxDuration() { - long[] chunkTimeBoundariesSec = {0, 5, 15, 45, 50}; - long[] chunkLengths = {10, 20, 30, 100}; - FakeMediaChunkIterator iterator = - new FakeMediaChunkIterator(chunkTimeBoundariesSec, chunkLengths); - - long maxDurationUs = 30 * C.MICROS_PER_SECOND; - int averageBitrate = TrackSelectionUtil.getAverageBitrate(iterator, maxDurationUs); - - assertThat(averageBitrate).isEqualTo(12); - } - - @Test - public void getAverageBitrate_zeroMaxDuration_returnsNoValue() { - long[] chunkTimeBoundariesSec = {0, 5, 10}; - long[] chunkLengths = {10, 20}; - - FakeMediaChunkIterator iterator = - new FakeMediaChunkIterator(chunkTimeBoundariesSec, chunkLengths); - - assertThat(TrackSelectionUtil.getAverageBitrate(iterator, /* maxDurationUs= */ 0)) - .isEqualTo(Format.NO_VALUE); - } - - @Test - public void getBitratesUsingFutureInfo_noIterator_returnsEmptyArray() { - assertThat( - TrackSelectionUtil.getBitratesUsingFutureInfo( - new MediaChunkIterator[0], new Format[0], MAX_DURATION_US, /* bitrates= */ null)) - .hasLength(0); - } - - @Test - public void getBitratesUsingFutureInfo_emptyIterator_returnsNoValue() { - int[] bitrates = - TrackSelectionUtil.getBitratesUsingFutureInfo( - new MediaChunkIterator[] {MediaChunkIterator.EMPTY}, - new Format[] {createFormatWithBitrate(10)}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(Format.NO_VALUE); - } - - @Test - public void getBitratesUsingFutureInfo_twoTracksZeroMaxDuration_returnsNoValue() { - FakeMediaChunkIterator iterator1 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 10}, /* chunkLengths= */ new long[] {10}); - FakeMediaChunkIterator iterator2 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 5, 15, 30}, - /* chunkLengths= */ new long[] {10, 20, 30}); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingFutureInfo( - new MediaChunkIterator[] {iterator1, iterator2}, - new Format[] {createFormatWithBitrate(10), createFormatWithBitrate(20)}, - /* maxDurationUs= */ 0, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(Format.NO_VALUE, Format.NO_VALUE); - } - - @Test - public void getBitratesUsingFutureInfo_twoTracks_returnsBitrates() { - FakeMediaChunkIterator iterator1 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 10}, /* chunkLengths= */ new long[] {10}); - FakeMediaChunkIterator iterator2 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 5, 15, 30}, - /* chunkLengths= */ new long[] {10, 20, 30}); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingFutureInfo( - new MediaChunkIterator[] {iterator1, iterator2}, - new Format[] {createFormatWithBitrate(10), createFormatWithBitrate(20)}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(8, 16).inOrder(); - } - - @Test - public void getBitratesUsingFutureInfo_bitratesArrayGiven_returnsTheSameArray() { - FakeMediaChunkIterator iterator1 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 10}, /* chunkLengths= */ new long[] {10}); - FakeMediaChunkIterator iterator2 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 5, 15, 30}, - /* chunkLengths= */ new long[] {10, 20, 30}); - - int[] bitratesArrayToUse = new int[2]; - int[] bitrates = - TrackSelectionUtil.getBitratesUsingFutureInfo( - new MediaChunkIterator[] {iterator1, iterator2}, - new Format[] {createFormatWithBitrate(10), createFormatWithBitrate(20)}, - MAX_DURATION_US, - bitratesArrayToUse); - - assertThat(bitrates).isSameInstanceAs(bitratesArrayToUse); - } - - @Test - public void getBitratesUsingFutureInfo_emptyIterator_returnsEstimationUsingClosest() { - FakeMediaChunkIterator iterator1 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 5}, /* chunkLengths= */ new long[] {10}); - Format format1 = createFormatWithBitrate(10); - MediaChunkIterator iterator2 = MediaChunkIterator.EMPTY; - Format format2 = createFormatWithBitrate(20); - FakeMediaChunkIterator iterator3 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 5}, /* chunkLengths= */ new long[] {50}); - Format format3 = createFormatWithBitrate(25); - FakeMediaChunkIterator iterator4 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 5}, /* chunkLengths= */ new long[] {20}); - Format format4 = createFormatWithBitrate(30); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingFutureInfo( - new MediaChunkIterator[] {iterator1, iterator2, iterator3, iterator4}, - new Format[] {format1, format2, format3, format4}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(16, 64, 80, 32).inOrder(); - } - - @Test - public void getBitratesUsingFutureInfo_formatWithoutBitrate_returnsNoValueForEmpty() { - FakeMediaChunkIterator iterator1 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 5}, /* chunkLengths= */ new long[] {10}); - Format format1 = createFormatWithBitrate(10); - MediaChunkIterator iterator2 = MediaChunkIterator.EMPTY; - Format format2 = createFormatWithBitrate(Format.NO_VALUE); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingFutureInfo( - new MediaChunkIterator[] {iterator1, iterator2}, - new Format[] {format1, format2}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(16, Format.NO_VALUE).inOrder(); - } - - @Test - public void getBitratesUsingPastInfo_noFormat_returnsEmptyArray() { - FakeMediaChunk chunk = - createChunk( - createFormatWithBitrate(10), - /* length= */ 10, - /* startTimeSec= */ 0, - /* endTimeSec= */ 10); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Collections.singletonList(chunk), new Format[0], MAX_DURATION_US, /* bitrates= */ null); - - assertThat(bitrates).hasLength(0); - } - - @Test - public void getBitratesUsingPastInfo_emptyQueue_returnsNoValue() { - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Collections.emptyList(), - new Format[] {createFormatWithBitrate(10)}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(Format.NO_VALUE); - } - - @Test - public void getBitratesUsingPastInfo_oneChunkFormatNoBitrate_returnsNoValue() { - Format format = createFormatWithBitrate(Format.NO_VALUE); - FakeMediaChunk chunk = - createChunk(format, /* length= */ 10, /* startTimeSec= */ 0, /* endTimeSec= */ 10); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Collections.singletonList(chunk), - new Format[] {format}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(Format.NO_VALUE); - } - - @Test - public void getBitratesUsingPastInfo_oneChunkNoLength_returnsNoValue() { - Format format = createFormatWithBitrate(10); - FakeMediaChunk chunk = - createChunk( - format, /* length= */ C.LENGTH_UNSET, /* startTimeSec= */ 0, /* endTimeSec= */ 10); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Collections.singletonList(chunk), - new Format[] {format}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(Format.NO_VALUE); - } - - @Test - public void getBitratesUsingPastInfo_oneChunkWithSameFormat_returnsBitrates() { - Format format = createFormatWithBitrate(10); - FakeMediaChunk chunk = - createChunk(format, /* length= */ 10, /* startTimeSec= */ 0, /* endTimeSec= */ 10); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Collections.singletonList(chunk), - new Format[] {format}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(8).inOrder(); - } - - @Test - public void getBitratesUsingPastInfo_zeroMaxDuration_returnsNoValue() { - Format format = createFormatWithBitrate(10); - FakeMediaChunk chunk = - createChunk(format, /* length= */ 10, /* startTimeSec= */ 0, /* endTimeSec= */ 10); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Collections.singletonList(chunk), - new Format[] {format}, - /* maxDurationUs= */ 0, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(Format.NO_VALUE).inOrder(); - } - - @Test - public void getBitratesUsingPastInfo_multipleChunkWithSameFormat_returnsAverageBitrate() { - Format format = createFormatWithBitrate(10); - FakeMediaChunk chunk = - createChunk(format, /* length= */ 10, /* startTimeSec= */ 0, /* endTimeSec= */ 10); - FakeMediaChunk chunk2 = - createChunk(format, /* length= */ 20, /* startTimeSec= */ 10, /* endTimeSec= */ 20); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Arrays.asList(chunk, chunk2), - new Format[] {format}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(12).inOrder(); - } - - @Test - public void getBitratesUsingPastInfo_oneChunkWithDifferentFormat_returnsEstimationBitrate() { - FakeMediaChunk chunk = - createChunk( - createFormatWithBitrate(10), - /* length= */ 10, - /* startTimeSec= */ 0, - /* endTimeSec= */ 10); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Collections.singletonList(chunk), - new Format[] {createFormatWithBitrate(20)}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(16).inOrder(); - } - - @Test - public void getBitratesUsingPastInfo_trackFormatNoBitrate_returnsNoValue() { - FakeMediaChunk chunk = - createChunk( - createFormatWithBitrate(10), - /* length= */ 10, - /* startTimeSec= */ 0, - /* endTimeSec= */ 10); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Collections.singletonList(chunk), - new Format[] {createFormatWithBitrate(Format.NO_VALUE)}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(Format.NO_VALUE); - } - - @Test - public void getBitratesUsingPastInfo_multipleTracks_returnsBitrates() { - FakeMediaChunk chunk = - createChunk( - createFormatWithBitrate(10), - /* length= */ 10, - /* startTimeSec= */ 0, - /* endTimeSec= */ 10); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Collections.singletonList(chunk), - new Format[] {createFormatWithBitrate(20), createFormatWithBitrate(30)}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(16, 24).inOrder(); - } - - @Test - public void getBitratesUsingPastInfo_bitratesArrayGiven_returnsTheSameArray() { - FakeMediaChunk chunk = - createChunk( - createFormatWithBitrate(10), - /* length= */ 10, - /* startTimeSec= */ 0, - /* endTimeSec= */ 10); - - int[] bitratesArrayToUse = new int[2]; - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Collections.singletonList(chunk), - new Format[] {createFormatWithBitrate(20), createFormatWithBitrate(30)}, - MAX_DURATION_US, - bitratesArrayToUse); - - assertThat(bitrates).isSameInstanceAs(bitratesArrayToUse); - } - - @Test - public void - getBitratesUsingPastInfo_multipleChunkExceedingMaxDuration_returnsAverageUntilMaxDuration() { - Format format = createFormatWithBitrate(10); - FakeMediaChunk chunk = - createChunk(format, /* length= */ 10, /* startTimeSec= */ 0, /* endTimeSec= */ 20); - FakeMediaChunk chunk2 = - createChunk(format, /* length= */ 40, /* startTimeSec= */ 20, /* endTimeSec= */ 40); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Arrays.asList(chunk, chunk2), - new Format[] {format}, - /* maxDurationUs= */ 30 * C.MICROS_PER_SECOND, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(12).inOrder(); - } - - @Test - public void - getBitratesUsingPastInfo_chunksWithDifferentFormats_returnsChunkAverageBitrateForLastFormat() { - FakeMediaChunk chunk = - createChunk( - createFormatWithBitrate(10), - /* length= */ 10, - /* startTimeSec= */ 0, - /* endTimeSec= */ 10); - FakeMediaChunk chunk2 = - createChunk( - createFormatWithBitrate(20), - /* length= */ 40, - /* startTimeSec= */ 10, - /* endTimeSec= */ 20); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastInfo( - Arrays.asList(chunk, chunk2), - new Format[] {createFormatWithBitrate(10)}, - MAX_DURATION_US, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(16).inOrder(); - } - - @Test - public void getBitratesUsingPastAndFutureInfo_noPastInfo_returnsBitratesUsingOnlyFutureInfo() { - FakeMediaChunkIterator iterator1 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 10}, /* chunkLengths= */ new long[] {10}); - FakeMediaChunkIterator iterator2 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 5, 15, 30}, - /* chunkLengths= */ new long[] {10, 20, 30}); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastAndFutureInfo( - new Format[] {createFormatWithBitrate(10), createFormatWithBitrate(20)}, - Collections.emptyList(), - MAX_DURATION_US, - new MediaChunkIterator[] {iterator1, iterator2}, - MAX_DURATION_US, - /* useFormatBitrateAsLowerBound= */ false, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(8, 16).inOrder(); - } - - @Test - public void getBitratesUsingPastAndFutureInfo_noFutureInfo_returnsBitratesUsingOnlyPastInfo() { - FakeMediaChunk chunk = - createChunk( - createFormatWithBitrate(10), - /* length= */ 10, - /* startTimeSec= */ 0, - /* endTimeSec= */ 10); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastAndFutureInfo( - new Format[] {createFormatWithBitrate(20), createFormatWithBitrate(30)}, - Collections.singletonList(chunk), - MAX_DURATION_US, - new MediaChunkIterator[] {MediaChunkIterator.EMPTY, MediaChunkIterator.EMPTY}, - MAX_DURATION_US, - /* useFormatBitrateAsLowerBound= */ false, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(16, 24).inOrder(); - } - - @Test - public void - getBitratesUsingPastAndFutureInfo_pastAndFutureInfo_returnsBitratesUsingOnlyFutureInfo() { - FakeMediaChunk chunk = - createChunk( - createFormatWithBitrate(5), - /* length= */ 10, - /* startTimeSec= */ 0, - /* endTimeSec= */ 10); - FakeMediaChunkIterator iterator1 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 10}, /* chunkLengths= */ new long[] {10}); - FakeMediaChunkIterator iterator2 = - new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 5, 15, 30}, - /* chunkLengths= */ new long[] {10, 20, 30}); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastAndFutureInfo( - new Format[] {createFormatWithBitrate(10), createFormatWithBitrate(20)}, - Collections.singletonList(chunk), - MAX_DURATION_US, - new MediaChunkIterator[] {iterator1, iterator2}, - MAX_DURATION_US, - /* useFormatBitrateAsLowerBound= */ false, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(8, 16).inOrder(); - } - - @Test - public void getBitratesUsingPastAndFutureInfo_noPastAndFutureInfo_returnsBitratesOfFormats() { - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastAndFutureInfo( - new Format[] {createFormatWithBitrate(10), createFormatWithBitrate(20)}, - Collections.emptyList(), - MAX_DURATION_US, - new MediaChunkIterator[] {MediaChunkIterator.EMPTY, MediaChunkIterator.EMPTY}, - MAX_DURATION_US, - /* useFormatBitrateAsLowerBound= */ false, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(10, 20).inOrder(); - } - - @Test - public void - getBitratesUsingPastAndFutureInfo_estimatesLowerAndUseFormatBitrateAsLowerBoundTrue_returnsBitratesOfFormats() { - FakeMediaChunk chunk = - createChunk( - createFormatWithBitrate(10), - /* length= */ 10, - /* startTimeSec= */ 0, - /* endTimeSec= */ 10); - - int[] bitrates = - TrackSelectionUtil.getBitratesUsingPastAndFutureInfo( - new Format[] {createFormatWithBitrate(20), createFormatWithBitrate(30)}, - Collections.singletonList(chunk), - MAX_DURATION_US, - new MediaChunkIterator[] {MediaChunkIterator.EMPTY, MediaChunkIterator.EMPTY}, - MAX_DURATION_US, - /* useFormatBitrateAsLowerBound= */ true, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(20, 30).inOrder(); - } - - private static FakeMediaChunk createChunk( - Format format, int length, int startTimeSec, int endTimeSec) { - DataSpec dataSpec = - new DataSpec( - Uri.EMPTY, /* absoluteStreamPosition= */ 0, length, /* key= */ null, /* flags= */ 0); - return new FakeMediaChunk( - dataSpec, format, startTimeSec * C.MICROS_PER_SECOND, endTimeSec * C.MICROS_PER_SECOND); - } - - private static Format createFormatWithBitrate(int bitrate) { - return Format.createSampleFormat( - /* id= */ null, - /* sampleMimeType= */ null, - /* codecs= */ null, - bitrate, - /* drmInitData= */ null); - } -} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/WindowedTrackBitrateEstimatorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/WindowedTrackBitrateEstimatorTest.java deleted file mode 100644 index d40149baae..0000000000 --- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/WindowedTrackBitrateEstimatorTest.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.exoplayer2.trackselection; - -import static com.google.common.truth.Truth.assertThat; - -import android.net.Uri; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.source.chunk.MediaChunk; -import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; -import com.google.android.exoplayer2.testutil.FakeMediaChunk; -import com.google.android.exoplayer2.testutil.FakeMediaChunkIterator; -import com.google.android.exoplayer2.upstream.DataSpec; -import java.util.Collections; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** {@link WindowedTrackBitrateEstimator} tests. */ -@RunWith(AndroidJUnit4.class) -public class WindowedTrackBitrateEstimatorTest { - - private static final long MAX_DURATION_MS = 30_000; - - @Test - public void getBitrates_zeroMaxDuration_returnsFormatBitrates() { - WindowedTrackBitrateEstimator estimator = - new WindowedTrackBitrateEstimator( - /* maxPastDurationMs= */ 0, - /* maxFutureDurationMs= */ 0, - /* useFormatBitrateAsLowerBound= */ false); - MediaChunk chunk = createMediaChunk(/* formatBitrate= */ 5, /* actualBitrate= */ 10); - MediaChunkIterator iterator1 = createMediaChunkIteratorWithBitrate(8); - MediaChunkIterator iterator2 = createMediaChunkIteratorWithBitrate(16); - Format format1 = createFormatWithBitrate(10); - Format format2 = createFormatWithBitrate(20); - - int[] bitrates = - estimator.getBitrates( - new Format[] {format1, format2}, - Collections.singletonList(chunk), - new MediaChunkIterator[] {iterator1, iterator2}, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(10, 20).inOrder(); - } - - @Test - public void getBitrates_futureMaxDurationSet_returnsEstimateUsingFutureChunks() { - WindowedTrackBitrateEstimator estimator = - new WindowedTrackBitrateEstimator( - /* maxPastDurationMs= */ 0, MAX_DURATION_MS, /* useFormatBitrateAsLowerBound= */ false); - MediaChunk chunk = createMediaChunk(/* formatBitrate= */ 5, /* actualBitrate= */ 10); - MediaChunkIterator iterator1 = createMediaChunkIteratorWithBitrate(8); - MediaChunkIterator iterator2 = createMediaChunkIteratorWithBitrate(16); - Format format1 = createFormatWithBitrate(10); - Format format2 = createFormatWithBitrate(20); - - int[] bitrates = - estimator.getBitrates( - new Format[] {format1, format2}, - Collections.singletonList(chunk), - new MediaChunkIterator[] {iterator1, iterator2}, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(8, 16).inOrder(); - } - - @Test - public void getBitrates_pastMaxDurationSet_returnsEstimateUsingPastChunks() { - WindowedTrackBitrateEstimator estimator = - new WindowedTrackBitrateEstimator( - MAX_DURATION_MS, - /* maxFutureDurationMs= */ 0, - /* useFormatBitrateAsLowerBound= */ false); - MediaChunk chunk = createMediaChunk(/* formatBitrate= */ 5, /* actualBitrate= */ 10); - MediaChunkIterator iterator1 = createMediaChunkIteratorWithBitrate(8); - MediaChunkIterator iterator2 = createMediaChunkIteratorWithBitrate(16); - Format format1 = createFormatWithBitrate(10); - Format format2 = createFormatWithBitrate(20); - - int[] bitrates = - estimator.getBitrates( - new Format[] {format1, format2}, - Collections.singletonList(chunk), - new MediaChunkIterator[] {iterator1, iterator2}, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(16, 32).inOrder(); - } - - @Test - public void - getBitrates_useFormatBitrateAsLowerBoundSetTrue_returnsEstimateIfOnlyHigherThanFormat() { - WindowedTrackBitrateEstimator estimator = - new WindowedTrackBitrateEstimator( - MAX_DURATION_MS, MAX_DURATION_MS, /* useFormatBitrateAsLowerBound= */ true); - MediaChunk chunk = createMediaChunk(/* formatBitrate= */ 5, /* actualBitrate= */ 10); - MediaChunkIterator iterator1 = createMediaChunkIteratorWithBitrate(80); - MediaChunkIterator iterator2 = createMediaChunkIteratorWithBitrate(16); - Format format1 = createFormatWithBitrate(10); - Format format2 = createFormatWithBitrate(20); - - int[] bitrates = - estimator.getBitrates( - new Format[] {format1, format2}, - Collections.singletonList(chunk), - new MediaChunkIterator[] {iterator1, iterator2}, - /* bitrates= */ null); - - assertThat(bitrates).asList().containsExactly(80, 20).inOrder(); - } - - @Test - public void getBitrates_bitratesArrayGiven_returnsTheSameArray() { - WindowedTrackBitrateEstimator estimator = - new WindowedTrackBitrateEstimator( - MAX_DURATION_MS, MAX_DURATION_MS, /* useFormatBitrateAsLowerBound= */ true); - MediaChunk chunk = createMediaChunk(/* formatBitrate= */ 5, /* actualBitrate= */ 10); - MediaChunkIterator iterator1 = createMediaChunkIteratorWithBitrate(8); - MediaChunkIterator iterator2 = createMediaChunkIteratorWithBitrate(16); - Format format1 = createFormatWithBitrate(10); - Format format2 = createFormatWithBitrate(20); - - int[] bitratesArrayToUse = new int[2]; - int[] bitrates = - estimator.getBitrates( - new Format[] {format1, format2}, - Collections.singletonList(chunk), - new MediaChunkIterator[] {iterator1, iterator2}, - bitratesArrayToUse); - - assertThat(bitrates).isSameInstanceAs(bitratesArrayToUse); - } - - private static MediaChunk createMediaChunk(int formatBitrate, int actualBitrate) { - int length = actualBitrate / C.BITS_PER_BYTE; - DataSpec dataSpec = - new DataSpec( - Uri.EMPTY, /* absoluteStreamPosition= */ 0, length, /* key= */ null, /* flags= */ 0); - Format format = createFormatWithBitrate(formatBitrate); - return new FakeMediaChunk( - dataSpec, format, /* startTimeUs= */ 0L, /* endTimeUs= */ C.MICROS_PER_SECOND); - } - - private static Format createFormatWithBitrate(int bitrate) { - return Format.createSampleFormat( - /* id= */ null, - /* sampleMimeType= */ null, - /* codecs= */ null, - bitrate, - /* drmInitData= */ null); - } - - private static MediaChunkIterator createMediaChunkIteratorWithBitrate(int bitrate) { - return new FakeMediaChunkIterator( - /* chunkTimeBoundariesSec= */ new long[] {0, 1}, - /* chunkLengths= */ new long[] {bitrate / C.BITS_PER_BYTE}); - } -}