Commit 3e613beb by tonihei Committed by Ian Baker

Use time-to-first-byte in AdaptiveTrackSelection.

Knowing the time-to-first-byte allows to update the available
allocatable bandwidth to take this into account.

PiperOrigin-RevId: 363225573
parent 57796914
......@@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.trackselection;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
......@@ -318,11 +317,12 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
List<? extends MediaChunk> queue,
MediaChunkIterator[] mediaChunkIterators) {
long nowMs = clock.elapsedRealtime();
long chunkDurationUs = getChunkDurationUs(mediaChunkIterators, queue);
// Make initial selection
if (reason == C.SELECTION_REASON_UNKNOWN) {
reason = C.SELECTION_REASON_INITIAL;
selectedIndex = determineIdealSelectedIndex(nowMs);
selectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs);
return;
}
......@@ -334,7 +334,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
previousSelectedIndex = formatIndexOfPreviousChunk;
previousReason = Iterables.getLast(queue).trackSelectionReason;
}
int newSelectedIndex = determineIdealSelectedIndex(nowMs);
int newSelectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs);
if (!isBlacklisted(previousSelectedIndex, nowMs)) {
// Revert back to the previous selection if conditions are not suitable for switching.
Format currentFormat = getFormat(previousSelectedIndex);
......@@ -394,7 +394,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
if (playoutBufferedDurationBeforeLastChunkUs < minDurationToRetainAfterDiscardUs) {
return queueSize;
}
int idealSelectedIndex = determineIdealSelectedIndex(nowMs);
int idealSelectedIndex = determineIdealSelectedIndex(nowMs, getChunkDurationUs(queue));
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
......@@ -459,9 +459,11 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
*
* @param nowMs The current time in the timebase of {@link Clock#elapsedRealtime()}, or {@link
* Long#MIN_VALUE} to ignore track exclusion.
* @param chunkDurationUs The duration of a media chunk in microseconds, or {@link C#TIME_UNSET}
* if unknown.
*/
private int determineIdealSelectedIndex(long nowMs) {
long effectiveBitrate = getAllocatedBandwidth();
private int determineIdealSelectedIndex(long nowMs, long chunkDurationUs) {
long effectiveBitrate = getAllocatedBandwidth(chunkDurationUs);
int lowestBitrateAllowedIndex = 0;
for (int i = 0; i < length; i++) {
if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) {
......@@ -484,9 +486,35 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
: minDurationForQualityIncreaseUs;
}
private long getAllocatedBandwidth() {
long totalBandwidth =
(long) (bandwidthMeter.getBitrateEstimate() * bandwidthFraction / playbackSpeed);
private long getChunkDurationUs(
MediaChunkIterator[] mediaChunkIterators, List<? extends MediaChunk> queue) {
// First, try to get the chunk duration for currently selected format.
if (selectedIndex < mediaChunkIterators.length && mediaChunkIterators[selectedIndex].next()) {
MediaChunkIterator iterator = mediaChunkIterators[selectedIndex];
return iterator.getChunkEndTimeUs() - iterator.getChunkStartTimeUs();
}
// Second, try to get the chunk duration for another format.
for (MediaChunkIterator iterator : mediaChunkIterators) {
if (iterator.next()) {
return iterator.getChunkEndTimeUs() - iterator.getChunkStartTimeUs();
}
}
// Third, try to get chunk duration for previous chunk in the queue.
return getChunkDurationUs(queue);
}
private long getChunkDurationUs(List<? extends MediaChunk> queue) {
if (queue.isEmpty()) {
return C.TIME_UNSET;
}
MediaChunk lastChunk = Iterables.getLast(queue);
return lastChunk.startTimeUs != C.TIME_UNSET && lastChunk.endTimeUs != C.TIME_UNSET
? lastChunk.endTimeUs - lastChunk.startTimeUs
: C.TIME_UNSET;
}
private long getAllocatedBandwidth(long chunkDurationUs) {
long totalBandwidth = getTotalAllocatableBandwidth(chunkDurationUs);
if (adaptationCheckpoints.isEmpty()) {
return totalBandwidth;
}
......@@ -505,6 +533,17 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
(fractionBetweenCheckpoints * (next.allocatedBandwidth - previous.allocatedBandwidth));
}
private long getTotalAllocatableBandwidth(long chunkDurationUs) {
long cautiousBandwidthEstimate =
(long) (bandwidthMeter.getBitrateEstimate() * bandwidthFraction);
long timeToFirstByteEstimateUs = bandwidthMeter.getTimeToFirstByteEstimateUs();
if (timeToFirstByteEstimateUs == C.TIME_UNSET || chunkDurationUs == C.TIME_UNSET) {
return (long) (cautiousBandwidthEstimate / playbackSpeed);
}
float availableTimeToLoadUs = chunkDurationUs / playbackSpeed - timeToFirstByteEstimateUs;
return (long) (cautiousBandwidthEstimate * availableTimeToLoadUs / chunkDurationUs);
}
/**
* Returns adaptation checkpoints for allocating bandwidth for adaptive track selections.
*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment