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 @@ ...@@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer2.trackselection; package com.google.android.exoplayer2.trackselection;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
...@@ -318,11 +317,12 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { ...@@ -318,11 +317,12 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
List<? extends MediaChunk> queue, List<? extends MediaChunk> queue,
MediaChunkIterator[] mediaChunkIterators) { MediaChunkIterator[] mediaChunkIterators) {
long nowMs = clock.elapsedRealtime(); long nowMs = clock.elapsedRealtime();
long chunkDurationUs = getChunkDurationUs(mediaChunkIterators, queue);
// Make initial selection // Make initial selection
if (reason == C.SELECTION_REASON_UNKNOWN) { if (reason == C.SELECTION_REASON_UNKNOWN) {
reason = C.SELECTION_REASON_INITIAL; reason = C.SELECTION_REASON_INITIAL;
selectedIndex = determineIdealSelectedIndex(nowMs); selectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs);
return; return;
} }
...@@ -334,7 +334,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { ...@@ -334,7 +334,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
previousSelectedIndex = formatIndexOfPreviousChunk; previousSelectedIndex = formatIndexOfPreviousChunk;
previousReason = Iterables.getLast(queue).trackSelectionReason; previousReason = Iterables.getLast(queue).trackSelectionReason;
} }
int newSelectedIndex = determineIdealSelectedIndex(nowMs); int newSelectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs);
if (!isBlacklisted(previousSelectedIndex, nowMs)) { if (!isBlacklisted(previousSelectedIndex, nowMs)) {
// Revert back to the previous selection if conditions are not suitable for switching. // Revert back to the previous selection if conditions are not suitable for switching.
Format currentFormat = getFormat(previousSelectedIndex); Format currentFormat = getFormat(previousSelectedIndex);
...@@ -394,7 +394,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { ...@@ -394,7 +394,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
if (playoutBufferedDurationBeforeLastChunkUs < minDurationToRetainAfterDiscardUs) { if (playoutBufferedDurationBeforeLastChunkUs < minDurationToRetainAfterDiscardUs) {
return queueSize; return queueSize;
} }
int idealSelectedIndex = determineIdealSelectedIndex(nowMs); int idealSelectedIndex = determineIdealSelectedIndex(nowMs, getChunkDurationUs(queue));
Format idealFormat = getFormat(idealSelectedIndex); Format idealFormat = getFormat(idealSelectedIndex);
// If the chunks contain video, discard from the first SD chunk beyond // If the chunks contain video, discard from the first SD chunk beyond
// minDurationToRetainAfterDiscardUs whose resolution and bitrate are both lower than the ideal // minDurationToRetainAfterDiscardUs whose resolution and bitrate are both lower than the ideal
...@@ -459,9 +459,11 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { ...@@ -459,9 +459,11 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
* *
* @param nowMs The current time in the timebase of {@link Clock#elapsedRealtime()}, or {@link * @param nowMs The current time in the timebase of {@link Clock#elapsedRealtime()}, or {@link
* Long#MIN_VALUE} to ignore track exclusion. * 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) { private int determineIdealSelectedIndex(long nowMs, long chunkDurationUs) {
long effectiveBitrate = getAllocatedBandwidth(); long effectiveBitrate = getAllocatedBandwidth(chunkDurationUs);
int lowestBitrateAllowedIndex = 0; int lowestBitrateAllowedIndex = 0;
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) { if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) {
...@@ -484,9 +486,35 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { ...@@ -484,9 +486,35 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
: minDurationForQualityIncreaseUs; : minDurationForQualityIncreaseUs;
} }
private long getAllocatedBandwidth() { private long getChunkDurationUs(
long totalBandwidth = MediaChunkIterator[] mediaChunkIterators, List<? extends MediaChunk> queue) {
(long) (bandwidthMeter.getBitrateEstimate() * bandwidthFraction / playbackSpeed); // 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()) { if (adaptationCheckpoints.isEmpty()) {
return totalBandwidth; return totalBandwidth;
} }
...@@ -505,6 +533,17 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { ...@@ -505,6 +533,17 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
(fractionBetweenCheckpoints * (next.allocatedBandwidth - previous.allocatedBandwidth)); (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. * 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