*** Original commit ***

Rollback of 0943886cbd

*** Original commit ***

Use last queue format instead of previous decision to select new track

We currently use the save...

***

PiperOrigin-RevId: 320015109
This commit is contained in:
tonihei 2020-07-07 18:46:40 +01:00 committed by kim-vde
parent 9b652ac14b
commit ebb9569624
4 changed files with 121 additions and 34 deletions

View File

@ -428,33 +428,35 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
return;
}
// Stash the current selection, then make a new one.
int currentSelectedIndex = selectedIndex;
selectedIndex = determineIdealSelectedIndex(nowMs);
if (selectedIndex == currentSelectedIndex) {
return;
int previousSelectedIndex = selectedIndex;
int previousReason = reason;
int formatIndexOfPreviousChunk =
queue.isEmpty() ? C.INDEX_UNSET : indexOf(Iterables.getLast(queue).trackFormat);
if (formatIndexOfPreviousChunk != C.INDEX_UNSET) {
previousSelectedIndex = formatIndexOfPreviousChunk;
previousReason = Iterables.getLast(queue).trackSelectionReason;
}
if (!isBlacklisted(currentSelectedIndex, nowMs)) {
// Revert back to the current selection if conditions are not suitable for switching.
Format currentFormat = getFormat(currentSelectedIndex);
Format selectedFormat = getFormat(selectedIndex);
int newSelectedIndex = determineIdealSelectedIndex(nowMs);
if (!isBlacklisted(previousSelectedIndex, nowMs)) {
// Revert back to the previous selection if conditions are not suitable for switching.
Format currentFormat = getFormat(previousSelectedIndex);
Format selectedFormat = getFormat(newSelectedIndex);
if (selectedFormat.bitrate > currentFormat.bitrate
&& bufferedDurationUs < minDurationForQualityIncreaseUs(availableDurationUs)) {
// The selected track is a higher quality, but we have insufficient buffer to safely switch
// up. Defer switching up for now.
selectedIndex = currentSelectedIndex;
newSelectedIndex = previousSelectedIndex;
} else if (selectedFormat.bitrate < currentFormat.bitrate
&& bufferedDurationUs >= maxDurationForQualityDecreaseUs) {
// The selected track is a lower quality, but we have sufficient buffer to defer switching
// down for now.
selectedIndex = currentSelectedIndex;
newSelectedIndex = previousSelectedIndex;
}
}
// If we adapted, update the trigger.
if (selectedIndex != currentSelectedIndex) {
reason = C.SELECTION_REASON_ADAPTIVE;
}
reason =
newSelectedIndex == previousSelectedIndex ? previousReason : C.SELECTION_REASON_ADAPTIVE;
selectedIndex = newSelectedIndex;
}
@Override

View File

@ -24,7 +24,6 @@ import com.google.android.exoplayer2.source.chunk.MediaChunk;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
@ -69,7 +68,8 @@ public abstract class BaseTrackSelection implements TrackSelection {
for (int i = 0; i < tracks.length; i++) {
formats[i] = group.getFormat(tracks[i]);
}
Arrays.sort(formats, new DecreasingBandwidthComparator());
// Sort in order of decreasing bandwidth.
Arrays.sort(formats, (a, b) -> b.bitrate - a.bitrate);
// Set the format indices in the same order.
this.tracks = new int[length];
for (int i = 0; i < length; i++) {
@ -199,17 +199,4 @@ public abstract class BaseTrackSelection implements TrackSelection {
BaseTrackSelection other = (BaseTrackSelection) obj;
return group == other.group && Arrays.equals(tracks, other.tracks);
}
/**
* Sorts {@link Format} objects in order of decreasing bandwidth.
*/
private static final class DecreasingBandwidthComparator implements Comparator<Format> {
@Override
public int compare(Format a, Format b) {
return b.bitrate - a.bitrate;
}
}
}

View File

@ -33,6 +33,7 @@ import com.google.android.exoplayer2.testutil.FakeMediaChunk;
import com.google.android.exoplayer2.trackselection.TrackSelection.Definition;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -328,6 +329,88 @@ public final class AdaptiveTrackSelectionTest {
assertThat(newSize).isEqualTo(2);
}
@Test
public void updateSelectedTrack_usesFormatOfLastChunkInTheQueueForSelection() {
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
TrackGroup trackGroup = new TrackGroup(format1, format2);
adaptiveTrackSelection =
new AdaptiveTrackSelection.Factory(
/* minDurationForQualityIncreaseMs= */ 10_000,
/* maxDurationForQualityDecreaseMs= */ 10_000,
/* minDurationToRetainAfterDiscardMs= */ 25_000,
/* bandwidthFraction= */ 1f)
.createAdaptiveTrackSelection(
trackGroup,
mockBandwidthMeter,
/* tracks= */ new int[] {0, 1},
/* totalFixedTrackBandwidth= */ 0);
// Make initial selection.
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L);
prepareTrackSelection(adaptiveTrackSelection);
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_INITIAL);
// Ensure that track selection wants to switch down due to low bandwidth.
FakeMediaChunk chunk1 =
new FakeMediaChunk(
format2, /* startTimeUs= */ 0, /* endTimeUs= */ 2_000_000, C.SELECTION_REASON_INITIAL);
FakeMediaChunk chunk2 =
new FakeMediaChunk(
format2,
/* startTimeUs= */ 2_000_000,
/* endTimeUs= */ 4_000_000,
C.SELECTION_REASON_INITIAL);
List<FakeMediaChunk> queue = ImmutableList.of(chunk1, chunk2);
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(500L);
adaptiveTrackSelection.updateSelectedTrack(
/* playbackPositionUs= */ 0,
/* bufferedDurationUs= */ 4_000_000,
/* availableDurationUs= */ C.TIME_UNSET,
queue,
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format1);
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_ADAPTIVE);
// Assert that an improved bandwidth selects the last chunk's format and ignores the previous
// decision. Switching up from the previous decision wouldn't be possible yet because the
// buffered duration is less than minDurationForQualityIncreaseMs.
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L);
adaptiveTrackSelection.updateSelectedTrack(
/* playbackPositionUs= */ 0,
/* bufferedDurationUs= */ 4_000_000,
/* availableDurationUs= */ C.TIME_UNSET,
queue,
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_INITIAL);
}
@Test
public void updateSelectedTrack_withQueueOfUnknownFormats_doesntThrow() {
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
TrackGroup trackGroup = new TrackGroup(format1, format2);
adaptiveTrackSelection = prepareTrackSelection(adaptiveTrackSelection(trackGroup));
Format unknownFormat = videoFormat(/* bitrate= */ 42, /* width= */ 300, /* height= */ 123);
FakeMediaChunk chunk =
new FakeMediaChunk(unknownFormat, /* startTimeUs= */ 0, /* endTimeUs= */ 2_000_000);
List<FakeMediaChunk> queue = ImmutableList.of(chunk);
adaptiveTrackSelection.updateSelectedTrack(
/* playbackPositionUs= */ 0,
/* bufferedDurationUs= */ 2_000_000,
/* availableDurationUs= */ C.TIME_UNSET,
queue,
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
assertThat(adaptiveTrackSelection.getSelectedFormat()).isAnyOf(format1, format2);
}
private AdaptiveTrackSelection adaptiveTrackSelection(TrackGroup trackGroup) {
return adaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
trackGroup, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS);

View File

@ -28,16 +28,31 @@ public final class FakeMediaChunk extends MediaChunk {
private static final DataSource DATA_SOURCE = new DefaultHttpDataSource("TEST_AGENT");
/**
* Creates a fake media chunk.
*
* @param trackFormat The {@link Format}.
* @param startTimeUs The start time of the media, in microseconds.
* @param endTimeUs The end time of the media, in microseconds.
*/
public FakeMediaChunk(Format trackFormat, long startTimeUs, long endTimeUs) {
this(new DataSpec(Uri.EMPTY), trackFormat, startTimeUs, endTimeUs);
this(trackFormat, startTimeUs, endTimeUs, C.SELECTION_REASON_UNKNOWN);
}
public FakeMediaChunk(DataSpec dataSpec, Format trackFormat, long startTimeUs, long endTimeUs) {
/**
* Creates a fake media chunk.
*
* @param trackFormat The {@link Format}.
* @param startTimeUs The start time of the media, in microseconds.
* @param endTimeUs The end time of the media, in microseconds.
* @param selectionReason The reason for selecting this format.
*/
public FakeMediaChunk(Format trackFormat, long startTimeUs, long endTimeUs, int selectionReason) {
super(
DATA_SOURCE,
dataSpec,
new DataSpec(Uri.EMPTY),
trackFormat,
C.SELECTION_REASON_ADAPTIVE,
selectionReason,
/* trackSelectionData= */ null,
startTimeUs,
endTimeUs,