mirror of
https://github.com/androidx/media.git
synced 2025-05-09 00:20:45 +08:00
Rollback of 75eab31d79
*** 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:
parent
9b652ac14b
commit
ebb9569624
@ -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
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user