*** 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; return;
} }
// Stash the current selection, then make a new one. int previousSelectedIndex = selectedIndex;
int currentSelectedIndex = selectedIndex; int previousReason = reason;
selectedIndex = determineIdealSelectedIndex(nowMs); int formatIndexOfPreviousChunk =
if (selectedIndex == currentSelectedIndex) { queue.isEmpty() ? C.INDEX_UNSET : indexOf(Iterables.getLast(queue).trackFormat);
return; if (formatIndexOfPreviousChunk != C.INDEX_UNSET) {
previousSelectedIndex = formatIndexOfPreviousChunk;
previousReason = Iterables.getLast(queue).trackSelectionReason;
} }
int newSelectedIndex = determineIdealSelectedIndex(nowMs);
if (!isBlacklisted(currentSelectedIndex, nowMs)) { if (!isBlacklisted(previousSelectedIndex, nowMs)) {
// Revert back to the current selection if conditions are not suitable for switching. // Revert back to the previous selection if conditions are not suitable for switching.
Format currentFormat = getFormat(currentSelectedIndex); Format currentFormat = getFormat(previousSelectedIndex);
Format selectedFormat = getFormat(selectedIndex); Format selectedFormat = getFormat(newSelectedIndex);
if (selectedFormat.bitrate > currentFormat.bitrate if (selectedFormat.bitrate > currentFormat.bitrate
&& bufferedDurationUs < minDurationForQualityIncreaseUs(availableDurationUs)) { && bufferedDurationUs < minDurationForQualityIncreaseUs(availableDurationUs)) {
// The selected track is a higher quality, but we have insufficient buffer to safely switch // The selected track is a higher quality, but we have insufficient buffer to safely switch
// up. Defer switching up for now. // up. Defer switching up for now.
selectedIndex = currentSelectedIndex; newSelectedIndex = previousSelectedIndex;
} else if (selectedFormat.bitrate < currentFormat.bitrate } else if (selectedFormat.bitrate < currentFormat.bitrate
&& bufferedDurationUs >= maxDurationForQualityDecreaseUs) { && bufferedDurationUs >= maxDurationForQualityDecreaseUs) {
// The selected track is a lower quality, but we have sufficient buffer to defer switching // The selected track is a lower quality, but we have sufficient buffer to defer switching
// down for now. // down for now.
selectedIndex = currentSelectedIndex; newSelectedIndex = previousSelectedIndex;
} }
} }
// If we adapted, update the trigger. // If we adapted, update the trigger.
if (selectedIndex != currentSelectedIndex) { reason =
reason = C.SELECTION_REASON_ADAPTIVE; newSelectedIndex == previousSelectedIndex ? previousReason : C.SELECTION_REASON_ADAPTIVE;
} selectedIndex = newSelectedIndex;
} }
@Override @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.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator;
import java.util.List; import java.util.List;
/** /**
@ -69,7 +68,8 @@ public abstract class BaseTrackSelection implements TrackSelection {
for (int i = 0; i < tracks.length; i++) { for (int i = 0; i < tracks.length; i++) {
formats[i] = group.getFormat(tracks[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. // Set the format indices in the same order.
this.tracks = new int[length]; this.tracks = new int[length];
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
@ -199,17 +199,4 @@ public abstract class BaseTrackSelection implements TrackSelection {
BaseTrackSelection other = (BaseTrackSelection) obj; BaseTrackSelection other = (BaseTrackSelection) obj;
return group == other.group && Arrays.equals(tracks, other.tracks); 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.trackselection.TrackSelection.Definition;
import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -328,6 +329,88 @@ public final class AdaptiveTrackSelectionTest {
assertThat(newSize).isEqualTo(2); 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) { private AdaptiveTrackSelection adaptiveTrackSelection(TrackGroup trackGroup) {
return adaptiveTrackSelectionWithMinDurationForQualityIncreaseMs( return adaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
trackGroup, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS); 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"); 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) { 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( super(
DATA_SOURCE, DATA_SOURCE,
dataSpec, new DataSpec(Uri.EMPTY),
trackFormat, trackFormat,
C.SELECTION_REASON_ADAPTIVE, selectionReason,
/* trackSelectionData= */ null, /* trackSelectionData= */ null,
startTimeUs, startTimeUs,
endTimeUs, endTimeUs,