mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Clean up parallel adaptation code.
- The AdaptiveTrackSelection doesn't need to use the experimental terminolgy because the code is always triggered if there are multiple adaptive selections. - It's also confusing to pass the state on the outside after the object creation, so moving everything into a simple control flow again where the adaptation checkpoints are passed in via the constructor. - Instead of triple arrays, we can use more readable named structures. - The calculation of the checkpoints can be cleaned up to be more readable by moving things to helper methods. - The reserved bandwidth from all fixed track selections is really just a special case of multiple parallel adaptataions. So this logic doesn't need to be separate. - The whole logic also didn't have test coverage so far. Added tests for the actual adaptation using these checkpoints and the builder calculating the checkpoints. Overall this should be a no-op change. PiperOrigin-RevId: 350162834
This commit is contained in:
parent
0901fe6e38
commit
a4fbc2c98d
2 changed files with 419 additions and 234 deletions
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.trackselection;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Nullable;
|
||||
|
|
@ -27,11 +26,14 @@ import com.google.android.exoplayer2.source.TrackGroup;
|
|||
import com.google.android.exoplayer2.source.chunk.MediaChunk;
|
||||
import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
|
||||
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Clock;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
|
||||
|
|
@ -135,48 +137,23 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
BandwidthMeter bandwidthMeter,
|
||||
MediaPeriodId mediaPeriodId,
|
||||
Timeline timeline) {
|
||||
ImmutableList<ImmutableList<AdaptationCheckpoint>> adaptationCheckpoints =
|
||||
getAdaptationCheckpoints(definitions);
|
||||
TrackSelection[] selections = new TrackSelection[definitions.length];
|
||||
int totalFixedBandwidth = 0;
|
||||
for (int i = 0; i < definitions.length; i++) {
|
||||
Definition definition = definitions[i];
|
||||
if (definition != null && definition.tracks.length == 1) {
|
||||
// Make fixed selections first to know their total bandwidth.
|
||||
selections[i] =
|
||||
new FixedTrackSelection(
|
||||
definition.group, definition.tracks[0], definition.reason, definition.data);
|
||||
int trackBitrate = definition.group.getFormat(definition.tracks[0]).bitrate;
|
||||
if (trackBitrate != Format.NO_VALUE) {
|
||||
totalFixedBandwidth += trackBitrate;
|
||||
}
|
||||
}
|
||||
}
|
||||
List<AdaptiveTrackSelection> adaptiveSelections = new ArrayList<>();
|
||||
for (int i = 0; i < definitions.length; i++) {
|
||||
Definition definition = definitions[i];
|
||||
if (definition != null && definition.tracks.length > 1) {
|
||||
AdaptiveTrackSelection adaptiveSelection =
|
||||
createAdaptiveTrackSelection(
|
||||
definition.group, bandwidthMeter, definition.tracks, totalFixedBandwidth);
|
||||
adaptiveSelections.add(adaptiveSelection);
|
||||
selections[i] = adaptiveSelection;
|
||||
}
|
||||
}
|
||||
if (adaptiveSelections.size() > 1) {
|
||||
long[][] adaptiveTrackBitrates = new long[adaptiveSelections.size()][];
|
||||
for (int i = 0; i < adaptiveSelections.size(); i++) {
|
||||
AdaptiveTrackSelection adaptiveSelection = adaptiveSelections.get(i);
|
||||
adaptiveTrackBitrates[i] = new long[adaptiveSelection.length()];
|
||||
for (int j = 0; j < adaptiveSelection.length(); j++) {
|
||||
adaptiveTrackBitrates[i][j] =
|
||||
adaptiveSelection.getFormat(adaptiveSelection.length() - j - 1).bitrate;
|
||||
}
|
||||
}
|
||||
long[][][] bandwidthCheckpoints = getAllocationCheckpoints(adaptiveTrackBitrates);
|
||||
for (int i = 0; i < adaptiveSelections.size(); i++) {
|
||||
adaptiveSelections
|
||||
.get(i)
|
||||
.experimentalSetBandwidthAllocationCheckpoints(bandwidthCheckpoints[i]);
|
||||
@Nullable Definition definition = definitions[i];
|
||||
if (definition == null || definition.tracks.length == 0) {
|
||||
continue;
|
||||
}
|
||||
selections[i] =
|
||||
definition.tracks.length == 1
|
||||
? new FixedTrackSelection(
|
||||
definition.group, definition.tracks[0], definition.reason, definition.data)
|
||||
: createAdaptiveTrackSelection(
|
||||
definition.group,
|
||||
bandwidthMeter,
|
||||
definition.tracks,
|
||||
adaptationCheckpoints.get(i));
|
||||
}
|
||||
return selections;
|
||||
}
|
||||
|
|
@ -187,23 +164,25 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
* @param group The {@link TrackGroup}.
|
||||
* @param bandwidthMeter A {@link BandwidthMeter} which can be used to select tracks.
|
||||
* @param tracks The indices of the selected tracks in the track group.
|
||||
* @param totalFixedTrackBandwidth The total bandwidth used by all non-adaptive tracks, in bits
|
||||
* per second.
|
||||
* @param adaptationCheckpoints The {@link AdaptationCheckpoint checkpoints} that can be used to
|
||||
* calculate available bandwidth for this selection.
|
||||
* @return An {@link AdaptiveTrackSelection} for the specified tracks.
|
||||
*/
|
||||
protected AdaptiveTrackSelection createAdaptiveTrackSelection(
|
||||
TrackGroup group,
|
||||
BandwidthMeter bandwidthMeter,
|
||||
int[] tracks,
|
||||
int totalFixedTrackBandwidth) {
|
||||
ImmutableList<AdaptationCheckpoint> adaptationCheckpoints) {
|
||||
return new AdaptiveTrackSelection(
|
||||
group,
|
||||
tracks,
|
||||
new DefaultBandwidthProvider(bandwidthMeter, bandwidthFraction, totalFixedTrackBandwidth),
|
||||
bandwidthMeter,
|
||||
minDurationForQualityIncreaseMs,
|
||||
maxDurationForQualityDecreaseMs,
|
||||
minDurationToRetainAfterDiscardMs,
|
||||
bandwidthFraction,
|
||||
bufferedFractionToLiveEdgeForQualityIncrease,
|
||||
adaptationCheckpoints,
|
||||
clock);
|
||||
}
|
||||
}
|
||||
|
|
@ -216,11 +195,13 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
|
||||
private static final long MIN_TIME_BETWEEN_BUFFER_REEVALUTATION_MS = 1000;
|
||||
|
||||
private final BandwidthProvider bandwidthProvider;
|
||||
private final BandwidthMeter bandwidthMeter;
|
||||
private final long minDurationForQualityIncreaseUs;
|
||||
private final long maxDurationForQualityDecreaseUs;
|
||||
private final long minDurationToRetainAfterDiscardUs;
|
||||
private final float bandwidthFraction;
|
||||
private final float bufferedFractionToLiveEdgeForQualityIncrease;
|
||||
private final ImmutableList<AdaptationCheckpoint> adaptationCheckpoints;
|
||||
private final Clock clock;
|
||||
|
||||
private float playbackSpeed;
|
||||
|
|
@ -235,18 +216,17 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
* empty. May be in any order.
|
||||
* @param bandwidthMeter Provides an estimate of the currently available bandwidth.
|
||||
*/
|
||||
public AdaptiveTrackSelection(TrackGroup group, int[] tracks,
|
||||
BandwidthMeter bandwidthMeter) {
|
||||
public AdaptiveTrackSelection(TrackGroup group, int[] tracks, BandwidthMeter bandwidthMeter) {
|
||||
this(
|
||||
group,
|
||||
tracks,
|
||||
bandwidthMeter,
|
||||
/* reservedBandwidth= */ 0,
|
||||
DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS,
|
||||
DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
|
||||
DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
|
||||
DEFAULT_BANDWIDTH_FRACTION,
|
||||
DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE,
|
||||
/* adaptationCheckpoints= */ ImmutableList.of(),
|
||||
Clock.DEFAULT);
|
||||
}
|
||||
|
||||
|
|
@ -255,8 +235,6 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
* @param tracks The indices of the selected tracks within the {@link TrackGroup}. Must not be
|
||||
* empty. May be in any order.
|
||||
* @param bandwidthMeter Provides an estimate of the currently available bandwidth.
|
||||
* @param reservedBandwidth The reserved bandwidth, which shouldn't be considered available for
|
||||
* use, in bits per second.
|
||||
* @param minDurationForQualityIncreaseMs The minimum duration of buffered data required for the
|
||||
* selected track to switch to one of higher quality.
|
||||
* @param maxDurationForQualityDecreaseMs The maximum duration of buffered data required for the
|
||||
|
|
@ -274,62 +252,36 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
* when the playback position is closer to the live edge than {@code
|
||||
* minDurationForQualityIncreaseMs}, which would otherwise prevent switching to a higher
|
||||
* quality from happening.
|
||||
* @param adaptationCheckpoints The {@link AdaptationCheckpoint checkpoints} that can be used to
|
||||
* calculate available bandwidth for this selection.
|
||||
* @param clock The {@link Clock}.
|
||||
*/
|
||||
public AdaptiveTrackSelection(
|
||||
protected AdaptiveTrackSelection(
|
||||
TrackGroup group,
|
||||
int[] tracks,
|
||||
BandwidthMeter bandwidthMeter,
|
||||
long reservedBandwidth,
|
||||
long minDurationForQualityIncreaseMs,
|
||||
long maxDurationForQualityDecreaseMs,
|
||||
long minDurationToRetainAfterDiscardMs,
|
||||
float bandwidthFraction,
|
||||
float bufferedFractionToLiveEdgeForQualityIncrease,
|
||||
Clock clock) {
|
||||
this(
|
||||
group,
|
||||
tracks,
|
||||
new DefaultBandwidthProvider(bandwidthMeter, bandwidthFraction, reservedBandwidth),
|
||||
minDurationForQualityIncreaseMs,
|
||||
maxDurationForQualityDecreaseMs,
|
||||
minDurationToRetainAfterDiscardMs,
|
||||
bufferedFractionToLiveEdgeForQualityIncrease,
|
||||
clock);
|
||||
}
|
||||
|
||||
private AdaptiveTrackSelection(
|
||||
TrackGroup group,
|
||||
int[] tracks,
|
||||
BandwidthProvider bandwidthProvider,
|
||||
long minDurationForQualityIncreaseMs,
|
||||
long maxDurationForQualityDecreaseMs,
|
||||
long minDurationToRetainAfterDiscardMs,
|
||||
float bufferedFractionToLiveEdgeForQualityIncrease,
|
||||
List<AdaptationCheckpoint> adaptationCheckpoints,
|
||||
Clock clock) {
|
||||
super(group, tracks);
|
||||
this.bandwidthProvider = bandwidthProvider;
|
||||
this.bandwidthMeter = bandwidthMeter;
|
||||
this.minDurationForQualityIncreaseUs = minDurationForQualityIncreaseMs * 1000L;
|
||||
this.maxDurationForQualityDecreaseUs = maxDurationForQualityDecreaseMs * 1000L;
|
||||
this.minDurationToRetainAfterDiscardUs = minDurationToRetainAfterDiscardMs * 1000L;
|
||||
this.bandwidthFraction = bandwidthFraction;
|
||||
this.bufferedFractionToLiveEdgeForQualityIncrease =
|
||||
bufferedFractionToLiveEdgeForQualityIncrease;
|
||||
this.adaptationCheckpoints = ImmutableList.copyOf(adaptationCheckpoints);
|
||||
this.clock = clock;
|
||||
playbackSpeed = 1f;
|
||||
reason = C.SELECTION_REASON_UNKNOWN;
|
||||
lastBufferEvaluationMs = C.TIME_UNSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets checkpoints to determine the allocation bandwidth based on the total bandwidth.
|
||||
*
|
||||
* @param allocationCheckpoints List of checkpoints. Each element must be a long[2], with [0]
|
||||
* being the total bandwidth and [1] being the allocated bandwidth.
|
||||
*/
|
||||
public void experimentalSetBandwidthAllocationCheckpoints(long[][] allocationCheckpoints) {
|
||||
((DefaultBandwidthProvider) bandwidthProvider)
|
||||
.experimentalSetBandwidthAllocationCheckpoints(allocationCheckpoints);
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void enable() {
|
||||
|
|
@ -502,7 +454,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
* Long#MIN_VALUE} to ignore track exclusion.
|
||||
*/
|
||||
private int determineIdealSelectedIndex(long nowMs) {
|
||||
long effectiveBitrate = bandwidthProvider.getAllocatedBandwidth();
|
||||
long effectiveBitrate = getAllocatedBandwidth();
|
||||
int lowestBitrateAllowedIndex = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) {
|
||||
|
|
@ -525,162 +477,181 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
: minDurationForQualityIncreaseUs;
|
||||
}
|
||||
|
||||
/** Provides the allocated bandwidth. */
|
||||
private interface BandwidthProvider {
|
||||
|
||||
/** Returns the allocated bitrate. */
|
||||
long getAllocatedBandwidth();
|
||||
private long getAllocatedBandwidth() {
|
||||
long totalBandwidth = (long) (bandwidthMeter.getBitrateEstimate() * bandwidthFraction);
|
||||
if (adaptationCheckpoints.isEmpty()) {
|
||||
return totalBandwidth;
|
||||
}
|
||||
int nextIndex = 1;
|
||||
while (nextIndex < adaptationCheckpoints.size() - 1
|
||||
&& adaptationCheckpoints.get(nextIndex).totalBandwidth < totalBandwidth) {
|
||||
nextIndex++;
|
||||
}
|
||||
AdaptationCheckpoint previous = adaptationCheckpoints.get(nextIndex - 1);
|
||||
AdaptationCheckpoint next = adaptationCheckpoints.get(nextIndex);
|
||||
float fractionBetweenCheckpoints =
|
||||
(float) (totalBandwidth - previous.totalBandwidth)
|
||||
/ (next.totalBandwidth - previous.totalBandwidth);
|
||||
return previous.allocatedBandwidth
|
||||
+ (long)
|
||||
(fractionBetweenCheckpoints * (next.allocatedBandwidth - previous.allocatedBandwidth));
|
||||
}
|
||||
|
||||
private static final class DefaultBandwidthProvider implements BandwidthProvider {
|
||||
/**
|
||||
* Returns adaptation checkpoints for allocating bandwidth for adaptive track selections.
|
||||
*
|
||||
* @param definitions Array of track selection {@link Definition definitions}. Elements may be
|
||||
* null.
|
||||
* @return List of {@link AdaptationCheckpoint checkpoints} for each adaptive {@link Definition}
|
||||
* with more than one selected track.
|
||||
*/
|
||||
private static ImmutableList<ImmutableList<AdaptationCheckpoint>> getAdaptationCheckpoints(
|
||||
@NullableType Definition[] definitions) {
|
||||
List<ImmutableList.@NullableType Builder<AdaptationCheckpoint>> checkPointBuilders =
|
||||
new ArrayList<>();
|
||||
for (int i = 0; i < definitions.length; i++) {
|
||||
if (definitions[i] != null && definitions[i].tracks.length > 1) {
|
||||
ImmutableList.Builder<AdaptationCheckpoint> builder = ImmutableList.builder();
|
||||
// Add initial all-zero checkpoint.
|
||||
builder.add(new AdaptationCheckpoint(/* totalBandwidth= */ 0, /* allocatedBandwidth= */ 0));
|
||||
checkPointBuilders.add(builder);
|
||||
} else {
|
||||
checkPointBuilders.add(null);
|
||||
}
|
||||
}
|
||||
// Add minimum bitrate selection checkpoint.
|
||||
long[][] trackBitrates = getSortedTrackBitrates(definitions);
|
||||
int[] currentTrackIndices = new int[trackBitrates.length];
|
||||
long[] currentTrackBitrates = new long[trackBitrates.length];
|
||||
for (int i = 0; i < trackBitrates.length; i++) {
|
||||
currentTrackBitrates[i] = trackBitrates[i].length == 0 ? 0 : trackBitrates[i][0];
|
||||
}
|
||||
addCheckpoint(checkPointBuilders, currentTrackBitrates);
|
||||
// Iterate through all adaptive checkpoints.
|
||||
ImmutableList<Integer> switchOrder = getSwitchOrder(trackBitrates);
|
||||
for (int i = 0; i < switchOrder.size(); i++) {
|
||||
int switchIndex = switchOrder.get(i);
|
||||
int newTrackIndex = ++currentTrackIndices[switchIndex];
|
||||
currentTrackBitrates[switchIndex] = trackBitrates[switchIndex][newTrackIndex];
|
||||
addCheckpoint(checkPointBuilders, currentTrackBitrates);
|
||||
}
|
||||
// Add final checkpoint to extrapolate additional bandwidth for adaptive selections.
|
||||
for (int i = 0; i < definitions.length; i++) {
|
||||
if (checkPointBuilders.get(i) != null) {
|
||||
currentTrackBitrates[i] *= 2;
|
||||
}
|
||||
}
|
||||
addCheckpoint(checkPointBuilders, currentTrackBitrates);
|
||||
ImmutableList.Builder<ImmutableList<AdaptationCheckpoint>> output = ImmutableList.builder();
|
||||
for (int i = 0; i < checkPointBuilders.size(); i++) {
|
||||
@Nullable ImmutableList.Builder<AdaptationCheckpoint> builder = checkPointBuilders.get(i);
|
||||
output.add(builder == null ? ImmutableList.of() : builder.build());
|
||||
}
|
||||
return output.build();
|
||||
}
|
||||
|
||||
private final BandwidthMeter bandwidthMeter;
|
||||
private final float bandwidthFraction;
|
||||
private final long reservedBandwidth;
|
||||
/** Returns sorted track bitrates for all selected tracks. */
|
||||
private static long[][] getSortedTrackBitrates(@NullableType Definition[] definitions) {
|
||||
long[][] trackBitrates = new long[definitions.length][];
|
||||
for (int i = 0; i < definitions.length; i++) {
|
||||
@Nullable Definition definition = definitions[i];
|
||||
if (definition == null) {
|
||||
trackBitrates[i] = new long[0];
|
||||
continue;
|
||||
}
|
||||
trackBitrates[i] = new long[definition.tracks.length];
|
||||
for (int j = 0; j < definition.tracks.length; j++) {
|
||||
trackBitrates[i][j] = definition.group.getFormat(definition.tracks[j]).bitrate;
|
||||
}
|
||||
Arrays.sort(trackBitrates[i]);
|
||||
}
|
||||
return trackBitrates;
|
||||
}
|
||||
|
||||
@Nullable private long[][] allocationCheckpoints;
|
||||
/**
|
||||
* Returns order of track indices in which the respective track should be switched up.
|
||||
*
|
||||
* @param trackBitrates Sorted tracks bitrates for each selection.
|
||||
* @return List of track indices indicating in which order tracks should be switched up.
|
||||
*/
|
||||
private static ImmutableList<Integer> getSwitchOrder(long[][] trackBitrates) {
|
||||
// Algorithm:
|
||||
// 1. Use log bitrates to treat all bitrate update steps equally.
|
||||
// 2. Distribute switch points for each selection equally in the same [0.0-1.0] range.
|
||||
// 3. Switch up one format at a time in the order of the switch points.
|
||||
Multimap<Double, Integer> switchPoints = MultimapBuilder.treeKeys().arrayListValues().build();
|
||||
for (int i = 0; i < trackBitrates.length; i++) {
|
||||
if (trackBitrates[i].length <= 1) {
|
||||
continue;
|
||||
}
|
||||
double[] logBitrates = new double[trackBitrates[i].length];
|
||||
for (int j = 0; j < trackBitrates[i].length; j++) {
|
||||
logBitrates[j] = trackBitrates[i][j] == Format.NO_VALUE ? 0 : Math.log(trackBitrates[i][j]);
|
||||
}
|
||||
double totalBitrateDiff = logBitrates[logBitrates.length - 1] - logBitrates[0];
|
||||
for (int j = 0; j < logBitrates.length - 1; j++) {
|
||||
double switchBitrate = 0.5 * (logBitrates[j] + logBitrates[j + 1]);
|
||||
double switchPoint =
|
||||
totalBitrateDiff == 0.0 ? 1.0 : (switchBitrate - logBitrates[0]) / totalBitrateDiff;
|
||||
switchPoints.put(switchPoint, i);
|
||||
}
|
||||
}
|
||||
return ImmutableList.copyOf(switchPoints.values());
|
||||
}
|
||||
|
||||
/* package */ DefaultBandwidthProvider(
|
||||
BandwidthMeter bandwidthMeter, float bandwidthFraction, long reservedBandwidth) {
|
||||
this.bandwidthMeter = bandwidthMeter;
|
||||
this.bandwidthFraction = bandwidthFraction;
|
||||
this.reservedBandwidth = reservedBandwidth;
|
||||
/**
|
||||
* Add a checkpoint to the builders.
|
||||
*
|
||||
* @param checkPointBuilders Builders for adaptation checkpoints. May have null elements.
|
||||
* @param checkpointBitrates The bitrates of each track at this checkpoint.
|
||||
*/
|
||||
private static void addCheckpoint(
|
||||
List<ImmutableList.@NullableType Builder<AdaptationCheckpoint>> checkPointBuilders,
|
||||
long[] checkpointBitrates) {
|
||||
// Total bitrate includes all fixed tracks.
|
||||
long totalBitrate = 0;
|
||||
for (int i = 0; i < checkpointBitrates.length; i++) {
|
||||
totalBitrate += checkpointBitrates[i];
|
||||
}
|
||||
for (int i = 0; i < checkPointBuilders.size(); i++) {
|
||||
@Nullable ImmutableList.Builder<AdaptationCheckpoint> builder = checkPointBuilders.get(i);
|
||||
if (builder == null) {
|
||||
continue;
|
||||
}
|
||||
builder.add(
|
||||
new AdaptationCheckpoint(
|
||||
/* totalBandwidth= */ totalBitrate, /* allocatedBandwidth= */ checkpointBitrates[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/** Checkpoint to determine allocated bandwidth. */
|
||||
protected static final class AdaptationCheckpoint {
|
||||
|
||||
/** Total bandwidth in bits per second at which this checkpoint applies. */
|
||||
public final long totalBandwidth;
|
||||
/** Allocated bandwidth at this checkpoint in bits per second. */
|
||||
public final long allocatedBandwidth;
|
||||
|
||||
public AdaptationCheckpoint(long totalBandwidth, long allocatedBandwidth) {
|
||||
this.totalBandwidth = totalBandwidth;
|
||||
this.allocatedBandwidth = allocatedBandwidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAllocatedBandwidth() {
|
||||
long totalBandwidth = (long) (bandwidthMeter.getBitrateEstimate() * bandwidthFraction);
|
||||
long allocatableBandwidth = max(0L, totalBandwidth - reservedBandwidth);
|
||||
if (allocationCheckpoints == null) {
|
||||
return allocatableBandwidth;
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
int nextIndex = 1;
|
||||
while (nextIndex < allocationCheckpoints.length - 1
|
||||
&& allocationCheckpoints[nextIndex][0] < allocatableBandwidth) {
|
||||
nextIndex++;
|
||||
if (!(o instanceof AdaptationCheckpoint)) {
|
||||
return false;
|
||||
}
|
||||
long[] previous = allocationCheckpoints[nextIndex - 1];
|
||||
long[] next = allocationCheckpoints[nextIndex];
|
||||
float fractionBetweenCheckpoints =
|
||||
(float) (allocatableBandwidth - previous[0]) / (next[0] - previous[0]);
|
||||
return previous[1] + (long) (fractionBetweenCheckpoints * (next[1] - previous[1]));
|
||||
AdaptationCheckpoint that = (AdaptationCheckpoint) o;
|
||||
return totalBandwidth == that.totalBandwidth && allocatedBandwidth == that.allocatedBandwidth;
|
||||
}
|
||||
|
||||
/* package */ void experimentalSetBandwidthAllocationCheckpoints(
|
||||
long[][] allocationCheckpoints) {
|
||||
Assertions.checkArgument(allocationCheckpoints.length >= 2);
|
||||
this.allocationCheckpoints = allocationCheckpoints;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns allocation checkpoints for allocating bandwidth between multiple adaptive track
|
||||
* selections.
|
||||
*
|
||||
* @param trackBitrates Array of [selectionIndex][trackIndex] -> trackBitrate.
|
||||
* @return Array of allocation checkpoints [selectionIndex][checkpointIndex][2] with [0]=total
|
||||
* bandwidth at checkpoint and [1]=allocated bandwidth at checkpoint.
|
||||
*/
|
||||
private static long[][][] getAllocationCheckpoints(long[][] trackBitrates) {
|
||||
// Algorithm:
|
||||
// 1. Use log bitrates to treat all resolution update steps equally.
|
||||
// 2. Distribute switch points for each selection equally in the same [0.0-1.0] range.
|
||||
// 3. Switch up one format at a time in the order of the switch points.
|
||||
double[][] logBitrates = getLogArrayValues(trackBitrates);
|
||||
double[][] switchPoints = getSwitchPoints(logBitrates);
|
||||
|
||||
// There will be (count(switch point) + 3) checkpoints:
|
||||
// [0] = all zero, [1] = minimum bitrates, [2-(end-1)] = up-switch points,
|
||||
// [end] = extra point to set slope for additional bitrate.
|
||||
int checkpointCount = countArrayElements(switchPoints) + 3;
|
||||
long[][][] checkpoints = new long[logBitrates.length][checkpointCount][2];
|
||||
int[] currentSelection = new int[logBitrates.length];
|
||||
setCheckpointValues(checkpoints, /* checkpointIndex= */ 1, trackBitrates, currentSelection);
|
||||
for (int checkpointIndex = 2; checkpointIndex < checkpointCount - 1; checkpointIndex++) {
|
||||
int nextUpdateIndex = 0;
|
||||
double nextUpdateSwitchPoint = Double.MAX_VALUE;
|
||||
for (int i = 0; i < logBitrates.length; i++) {
|
||||
if (currentSelection[i] + 1 == logBitrates[i].length) {
|
||||
continue;
|
||||
}
|
||||
double switchPoint = switchPoints[i][currentSelection[i]];
|
||||
if (switchPoint < nextUpdateSwitchPoint) {
|
||||
nextUpdateSwitchPoint = switchPoint;
|
||||
nextUpdateIndex = i;
|
||||
}
|
||||
}
|
||||
currentSelection[nextUpdateIndex]++;
|
||||
setCheckpointValues(checkpoints, checkpointIndex, trackBitrates, currentSelection);
|
||||
}
|
||||
for (long[][] points : checkpoints) {
|
||||
points[checkpointCount - 1][0] = 2 * points[checkpointCount - 2][0];
|
||||
points[checkpointCount - 1][1] = 2 * points[checkpointCount - 2][1];
|
||||
}
|
||||
return checkpoints;
|
||||
}
|
||||
|
||||
/** Converts all input values to Math.log(value). */
|
||||
private static double[][] getLogArrayValues(long[][] values) {
|
||||
double[][] logValues = new double[values.length][];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
logValues[i] = new double[values[i].length];
|
||||
for (int j = 0; j < values[i].length; j++) {
|
||||
logValues[i][j] = values[i][j] == Format.NO_VALUE ? 0 : Math.log(values[i][j]);
|
||||
}
|
||||
}
|
||||
return logValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns idealized switch points for each switch between consecutive track selection bitrates.
|
||||
*
|
||||
* @param logBitrates Log bitrates with [selectionCount][formatCount].
|
||||
* @return Linearly distributed switch points in the range of [0.0-1.0].
|
||||
*/
|
||||
private static double[][] getSwitchPoints(double[][] logBitrates) {
|
||||
double[][] switchPoints = new double[logBitrates.length][];
|
||||
for (int i = 0; i < logBitrates.length; i++) {
|
||||
switchPoints[i] = new double[logBitrates[i].length - 1];
|
||||
if (switchPoints[i].length == 0) {
|
||||
continue;
|
||||
}
|
||||
double totalBitrateDiff = logBitrates[i][logBitrates[i].length - 1] - logBitrates[i][0];
|
||||
for (int j = 0; j < logBitrates[i].length - 1; j++) {
|
||||
double switchBitrate = 0.5 * (logBitrates[i][j] + logBitrates[i][j + 1]);
|
||||
switchPoints[i][j] =
|
||||
totalBitrateDiff == 0.0 ? 1.0 : (switchBitrate - logBitrates[i][0]) / totalBitrateDiff;
|
||||
}
|
||||
}
|
||||
return switchPoints;
|
||||
}
|
||||
|
||||
/** Returns total number of elements in a 2D array. */
|
||||
private static int countArrayElements(double[][] array) {
|
||||
int count = 0;
|
||||
for (double[] subArray : array) {
|
||||
count += subArray.length;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets checkpoint bitrates.
|
||||
*
|
||||
* @param checkpoints Output checkpoints with [selectionIndex][checkpointIndex][2] where [0]=Total
|
||||
* bitrate and [1]=Allocated bitrate.
|
||||
* @param checkpointIndex The checkpoint index.
|
||||
* @param trackBitrates The track bitrates with [selectionIndex][trackIndex].
|
||||
* @param selectedTracks The indices of selected tracks for each selection for this checkpoint.
|
||||
*/
|
||||
private static void setCheckpointValues(
|
||||
long[][][] checkpoints, int checkpointIndex, long[][] trackBitrates, int[] selectedTracks) {
|
||||
long totalBitrate = 0;
|
||||
for (int i = 0; i < checkpoints.length; i++) {
|
||||
checkpoints[i][checkpointIndex][1] = trackBitrates[i][selectedTracks[i]];
|
||||
totalBitrate += checkpoints[i][checkpointIndex][1];
|
||||
}
|
||||
for (long[][] points : checkpoints) {
|
||||
points[checkpointIndex][0] = totalBitrate;
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * (int) totalBandwidth + (int) allocatedBandwidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,15 @@ import static org.mockito.MockitoAnnotations.initMocks;
|
|||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
|
||||
import com.google.android.exoplayer2.testutil.FakeClock;
|
||||
import com.google.android.exoplayer2.testutil.FakeMediaChunk;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection.AdaptationCheckpoint;
|
||||
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;
|
||||
|
|
@ -312,7 +317,7 @@ public final class AdaptiveTrackSelectionTest {
|
|||
trackGroup,
|
||||
mockBandwidthMeter,
|
||||
/* tracks= */ new int[] {0, 1},
|
||||
/* totalFixedTrackBandwidth= */ 0);
|
||||
/* adaptationCheckpoints= */ ImmutableList.of());
|
||||
|
||||
// Make initial selection.
|
||||
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L);
|
||||
|
|
@ -380,6 +385,199 @@ public final class AdaptiveTrackSelectionTest {
|
|||
assertThat(adaptiveTrackSelection.getSelectedFormat()).isAnyOf(format1, format2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSelectedTrack_withAdaptationCheckpoints_usesOnlyAllocatedBandwidth() {
|
||||
Format format0 = videoFormat(/* bitrate= */ 100, /* width= */ 160, /* height= */ 120);
|
||||
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
|
||||
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
|
||||
Format format3 = videoFormat(/* bitrate= */ 1500, /* width= */ 1024, /* height= */ 768);
|
||||
TrackGroup trackGroup = new TrackGroup(format0, format1, format2, format3);
|
||||
// Choose checkpoints relative to formats so that one is in the first range, one somewhere in
|
||||
// the middle, and one needs to extrapolate beyond the last checkpoint.
|
||||
List<AdaptationCheckpoint> checkpoints =
|
||||
ImmutableList.of(
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 0, /* allocatedBandwidth= */ 0),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 1500, /* allocatedBandwidth= */ 750),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 3000, /* allocatedBandwidth= */ 750),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 4000, /* allocatedBandwidth= */ 1250),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 5000, /* allocatedBandwidth= */ 1300));
|
||||
AdaptiveTrackSelection adaptiveTrackSelection =
|
||||
prepareTrackSelection(
|
||||
adaptiveTrackSelectionWithAdaptationCheckpoints(trackGroup, checkpoints));
|
||||
|
||||
// Ensure format0 is selected initially so that we can assert the upswitches.
|
||||
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1L);
|
||||
adaptiveTrackSelection.updateSelectedTrack(
|
||||
/* playbackPositionUs= */ 0,
|
||||
/* bufferedDurationUs= */ 999_999_999_999L,
|
||||
/* availableDurationUs= */ C.TIME_UNSET,
|
||||
/* queue= */ ImmutableList.of(),
|
||||
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
|
||||
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format0);
|
||||
|
||||
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(999L);
|
||||
adaptiveTrackSelection.updateSelectedTrack(
|
||||
/* playbackPositionUs= */ 0,
|
||||
/* bufferedDurationUs= */ 999_999_999_999L,
|
||||
/* availableDurationUs= */ C.TIME_UNSET,
|
||||
/* queue= */ ImmutableList.of(),
|
||||
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
|
||||
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format0);
|
||||
|
||||
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L);
|
||||
adaptiveTrackSelection.updateSelectedTrack(
|
||||
/* playbackPositionUs= */ 0,
|
||||
/* bufferedDurationUs= */ 999_999_999_999L,
|
||||
/* availableDurationUs= */ C.TIME_UNSET,
|
||||
/* queue= */ ImmutableList.of(),
|
||||
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
|
||||
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format1);
|
||||
|
||||
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(2499L);
|
||||
adaptiveTrackSelection.updateSelectedTrack(
|
||||
/* playbackPositionUs= */ 0,
|
||||
/* bufferedDurationUs= */ 999_999_999_999L,
|
||||
/* availableDurationUs= */ C.TIME_UNSET,
|
||||
/* queue= */ ImmutableList.of(),
|
||||
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
|
||||
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format1);
|
||||
|
||||
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(3500L);
|
||||
adaptiveTrackSelection.updateSelectedTrack(
|
||||
/* playbackPositionUs= */ 0,
|
||||
/* bufferedDurationUs= */ 999_999_999_999L,
|
||||
/* availableDurationUs= */ C.TIME_UNSET,
|
||||
/* queue= */ ImmutableList.of(),
|
||||
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
|
||||
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
|
||||
|
||||
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(8999L);
|
||||
adaptiveTrackSelection.updateSelectedTrack(
|
||||
/* playbackPositionUs= */ 0,
|
||||
/* bufferedDurationUs= */ 999_999_999_999L,
|
||||
/* availableDurationUs= */ C.TIME_UNSET,
|
||||
/* queue= */ ImmutableList.of(),
|
||||
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
|
||||
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
|
||||
|
||||
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(9000L);
|
||||
adaptiveTrackSelection.updateSelectedTrack(
|
||||
/* playbackPositionUs= */ 0,
|
||||
/* bufferedDurationUs= */ 999_999_999_999L,
|
||||
/* availableDurationUs= */ C.TIME_UNSET,
|
||||
/* queue= */ ImmutableList.of(),
|
||||
/* mediaChunkIterators= */ THREE_EMPTY_MEDIA_CHUNK_ITERATORS);
|
||||
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
builderCreateTrackSelections_withSingleAdaptiveGroup_usesCorrectAdaptationCheckpoints() {
|
||||
Format formatFixed1 = new Format.Builder().setAverageBitrate(500).build();
|
||||
Format formatFixed2 = new Format.Builder().setAverageBitrate(1000).build();
|
||||
Format formatAdaptive1 = new Format.Builder().setAverageBitrate(2000).build();
|
||||
Format formatAdaptive2 = new Format.Builder().setAverageBitrate(3000).build();
|
||||
Format formatAdaptive3 = new Format.Builder().setAverageBitrate(4000).build();
|
||||
Format formatAdaptive4 = new Format.Builder().setAverageBitrate(5000).build();
|
||||
TrackGroup trackGroupMultipleFixed = new TrackGroup(formatFixed1, formatFixed2);
|
||||
TrackGroup trackGroupAdaptive =
|
||||
new TrackGroup(formatAdaptive1, formatAdaptive2, formatAdaptive3, formatAdaptive4);
|
||||
Definition definitionFixed1 = new Definition(trackGroupMultipleFixed, /* tracks...= */ 0);
|
||||
Definition definitionFixed2 = new Definition(trackGroupMultipleFixed, /* tracks...= */ 1);
|
||||
Definition definitionAdaptive = new Definition(trackGroupAdaptive, /* tracks...= */ 1, 2, 3);
|
||||
List<List<AdaptationCheckpoint>> checkPoints = new ArrayList<>();
|
||||
AdaptiveTrackSelection.Factory factory =
|
||||
new AdaptiveTrackSelection.Factory() {
|
||||
@Override
|
||||
protected AdaptiveTrackSelection createAdaptiveTrackSelection(
|
||||
TrackGroup group,
|
||||
BandwidthMeter bandwidthMeter,
|
||||
int[] tracks,
|
||||
ImmutableList<AdaptationCheckpoint> adaptationCheckpoints) {
|
||||
checkPoints.add(adaptationCheckpoints);
|
||||
return super.createAdaptiveTrackSelection(
|
||||
group, bandwidthMeter, tracks, adaptationCheckpoints);
|
||||
}
|
||||
};
|
||||
|
||||
Timeline timeline = new FakeTimeline();
|
||||
factory.createTrackSelections(
|
||||
new Definition[] {null, definitionFixed1, null, definitionFixed2, definitionAdaptive},
|
||||
mockBandwidthMeter,
|
||||
new MediaSource.MediaPeriodId(timeline.getUidOfPeriod(/* periodIndex= */ 0)),
|
||||
timeline);
|
||||
|
||||
assertThat(checkPoints).hasSize(1);
|
||||
assertThat(checkPoints.get(0))
|
||||
.containsExactly(
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 0, /* allocatedBandwidth= */ 0),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 4500, /* allocatedBandwidth= */ 3000),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 5500, /* allocatedBandwidth= */ 4000),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 6500, /* allocatedBandwidth= */ 5000),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 11500, /* allocatedBandwidth= */ 10000))
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
builderCreateTrackSelections_withMultipleAdaptiveGroups_usesCorrectAdaptationCheckpoints() {
|
||||
Format group1Format1 = new Format.Builder().setAverageBitrate(500).build();
|
||||
Format group1Format2 = new Format.Builder().setAverageBitrate(1000).build();
|
||||
Format group2Format1 = new Format.Builder().setAverageBitrate(250).build();
|
||||
Format group2Format2 = new Format.Builder().setAverageBitrate(500).build();
|
||||
Format group2Format3 = new Format.Builder().setAverageBitrate(1250).build();
|
||||
Format group2UnusedFormat = new Format.Builder().setAverageBitrate(2000).build();
|
||||
Format fixedFormat = new Format.Builder().setAverageBitrate(5000).build();
|
||||
TrackGroup trackGroup1 = new TrackGroup(group1Format1, group1Format2);
|
||||
TrackGroup trackGroup2 =
|
||||
new TrackGroup(group2Format1, group2Format2, group2Format3, group2UnusedFormat);
|
||||
TrackGroup fixedGroup = new TrackGroup(fixedFormat);
|
||||
Definition definition1 = new Definition(trackGroup1, /* tracks...= */ 0, 1);
|
||||
Definition definition2 = new Definition(trackGroup2, /* tracks...= */ 0, 1, 2);
|
||||
Definition fixedDefinition = new Definition(fixedGroup, /* tracks...= */ 0);
|
||||
List<List<AdaptationCheckpoint>> checkPoints = new ArrayList<>();
|
||||
AdaptiveTrackSelection.Factory factory =
|
||||
new AdaptiveTrackSelection.Factory() {
|
||||
@Override
|
||||
protected AdaptiveTrackSelection createAdaptiveTrackSelection(
|
||||
TrackGroup group,
|
||||
BandwidthMeter bandwidthMeter,
|
||||
int[] tracks,
|
||||
ImmutableList<AdaptationCheckpoint> adaptationCheckpoints) {
|
||||
checkPoints.add(adaptationCheckpoints);
|
||||
return super.createAdaptiveTrackSelection(
|
||||
group, bandwidthMeter, tracks, adaptationCheckpoints);
|
||||
}
|
||||
};
|
||||
|
||||
Timeline timeline = new FakeTimeline();
|
||||
factory.createTrackSelections(
|
||||
new Definition[] {null, definition1, fixedDefinition, definition2, null},
|
||||
mockBandwidthMeter,
|
||||
new MediaSource.MediaPeriodId(timeline.getUidOfPeriod(/* periodIndex= */ 0)),
|
||||
timeline);
|
||||
|
||||
assertThat(checkPoints).hasSize(2);
|
||||
assertThat(checkPoints.get(0))
|
||||
.containsExactly(
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 0, /* allocatedBandwidth= */ 0),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 5750, /* allocatedBandwidth= */ 500),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 6000, /* allocatedBandwidth= */ 500),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 6500, /* allocatedBandwidth= */ 1000),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 7250, /* allocatedBandwidth= */ 1000),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 9500, /* allocatedBandwidth= */ 2000))
|
||||
.inOrder();
|
||||
assertThat(checkPoints.get(1))
|
||||
.containsExactly(
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 0, /* allocatedBandwidth= */ 0),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 5750, /* allocatedBandwidth= */ 250),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 6000, /* allocatedBandwidth= */ 500),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 6500, /* allocatedBandwidth= */ 500),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 7250, /* allocatedBandwidth= */ 1250),
|
||||
new AdaptationCheckpoint(/* totalBandwidth= */ 9500, /* allocatedBandwidth= */ 2500))
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
private AdaptiveTrackSelection adaptiveTrackSelection(TrackGroup trackGroup) {
|
||||
return adaptiveTrackSelectionWithMinDurationForQualityIncreaseMs(
|
||||
trackGroup, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS);
|
||||
|
|
@ -392,12 +590,12 @@ public final class AdaptiveTrackSelectionTest {
|
|||
trackGroup,
|
||||
selectedAllTracksInGroup(trackGroup),
|
||||
mockBandwidthMeter,
|
||||
/* reservedBandwidth= */ 0,
|
||||
minDurationForQualityIncreaseMs,
|
||||
AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
|
||||
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
|
||||
/* bandwidthFraction= */ 1.0f,
|
||||
AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE,
|
||||
/* adaptationCheckpoints= */ ImmutableList.of(),
|
||||
fakeClock));
|
||||
}
|
||||
|
||||
|
|
@ -408,12 +606,12 @@ public final class AdaptiveTrackSelectionTest {
|
|||
trackGroup,
|
||||
selectedAllTracksInGroup(trackGroup),
|
||||
mockBandwidthMeter,
|
||||
/* reservedBandwidth= */ 0,
|
||||
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS,
|
||||
maxDurationForQualityDecreaseMs,
|
||||
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
|
||||
/* bandwidthFraction= */ 1.0f,
|
||||
AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE,
|
||||
/* adaptationCheckpoints= */ ImmutableList.of(),
|
||||
fakeClock));
|
||||
}
|
||||
|
||||
|
|
@ -424,12 +622,28 @@ public final class AdaptiveTrackSelectionTest {
|
|||
trackGroup,
|
||||
selectedAllTracksInGroup(trackGroup),
|
||||
mockBandwidthMeter,
|
||||
/* reservedBandwidth= */ 0,
|
||||
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS,
|
||||
AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
|
||||
durationToRetainAfterDiscardMs,
|
||||
/* bandwidthFraction= */ 1.0f,
|
||||
AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE,
|
||||
/* adaptationCheckpoints= */ ImmutableList.of(),
|
||||
fakeClock));
|
||||
}
|
||||
|
||||
private AdaptiveTrackSelection adaptiveTrackSelectionWithAdaptationCheckpoints(
|
||||
TrackGroup trackGroup, List<AdaptationCheckpoint> adaptationCheckpoints) {
|
||||
return prepareTrackSelection(
|
||||
new AdaptiveTrackSelection(
|
||||
trackGroup,
|
||||
selectedAllTracksInGroup(trackGroup),
|
||||
mockBandwidthMeter,
|
||||
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS,
|
||||
AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
|
||||
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
|
||||
/* bandwidthFraction= */ 1.0f,
|
||||
AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE,
|
||||
adaptationCheckpoints,
|
||||
fakeClock));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue