mirror of
https://github.com/samsonjs/media.git
synced 2026-04-01 10:35:48 +00:00
Update ABR logic in AdaptiveTrackSelection for live streaming case
In live streaming, if the playback position is very close to live edge, the buffered duration will never reach minDurationForQualityIncreaseMs, which prevents switching from ever happening. So we will provide the durationToLiveEdgeUs to AdaptiveTrackSelection in live streaming case, so it can handle this edge case. GitHub: #3017 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=168535969
This commit is contained in:
parent
39dbb9a7fc
commit
5019da3e7b
9 changed files with 141 additions and 26 deletions
|
|
@ -40,6 +40,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
private final int maxDurationForQualityDecreaseMs;
|
||||
private final int minDurationToRetainAfterDiscardMs;
|
||||
private final float bandwidthFraction;
|
||||
private final float bufferedFractionToLiveEdgeForQualityIncrease;
|
||||
|
||||
/**
|
||||
* @param bandwidthMeter Provides an estimate of the currently available bandwidth.
|
||||
|
|
@ -48,7 +49,9 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
this (bandwidthMeter, DEFAULT_MAX_INITIAL_BITRATE,
|
||||
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_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
|
||||
DEFAULT_BANDWIDTH_FRACTION,
|
||||
DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -70,19 +73,53 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
public Factory(BandwidthMeter bandwidthMeter, int maxInitialBitrate,
|
||||
int minDurationForQualityIncreaseMs, int maxDurationForQualityDecreaseMs,
|
||||
int minDurationToRetainAfterDiscardMs, float bandwidthFraction) {
|
||||
this (bandwidthMeter, maxInitialBitrate, minDurationForQualityIncreaseMs,
|
||||
maxDurationForQualityDecreaseMs, minDurationToRetainAfterDiscardMs,
|
||||
bandwidthFraction, DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bandwidthMeter Provides an estimate of the currently available bandwidth.
|
||||
* @param maxInitialBitrate The maximum bitrate in bits per second that should be assumed
|
||||
* when a bandwidth estimate is unavailable.
|
||||
* @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 selected track to switch to one of lower quality.
|
||||
* @param minDurationToRetainAfterDiscardMs When switching to a track of significantly higher
|
||||
* quality, the selection may indicate that media already buffered at the lower quality can
|
||||
* be discarded to speed up the switch. This is the minimum duration of media that must be
|
||||
* retained at the lower quality.
|
||||
* @param bandwidthFraction The fraction of the available bandwidth that the selection should
|
||||
* consider available for use. Setting to a value less than 1 is recommended to account
|
||||
* for inaccuracies in the bandwidth estimator.
|
||||
* @param bufferedFractionToLiveEdgeForQualityIncrease For live streaming, the fraction of
|
||||
* the duration from current playback position to the live edge that has to be buffered
|
||||
* before the selected track can be switched to one of higher quality. This parameter is
|
||||
* only applied when the playback position is closer to the live edge than
|
||||
* {@code minDurationForQualityIncreaseMs}, which would otherwise prevent switching to a
|
||||
* higher quality from happening.
|
||||
*/
|
||||
public Factory(BandwidthMeter bandwidthMeter, int maxInitialBitrate,
|
||||
int minDurationForQualityIncreaseMs, int maxDurationForQualityDecreaseMs,
|
||||
int minDurationToRetainAfterDiscardMs, float bandwidthFraction,
|
||||
float bufferedFractionToLiveEdgeForQualityIncrease) {
|
||||
this.bandwidthMeter = bandwidthMeter;
|
||||
this.maxInitialBitrate = maxInitialBitrate;
|
||||
this.minDurationForQualityIncreaseMs = minDurationForQualityIncreaseMs;
|
||||
this.maxDurationForQualityDecreaseMs = maxDurationForQualityDecreaseMs;
|
||||
this.minDurationToRetainAfterDiscardMs = minDurationToRetainAfterDiscardMs;
|
||||
this.bandwidthFraction = bandwidthFraction;
|
||||
this.bufferedFractionToLiveEdgeForQualityIncrease =
|
||||
bufferedFractionToLiveEdgeForQualityIncrease;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdaptiveTrackSelection createTrackSelection(TrackGroup group, int... tracks) {
|
||||
return new AdaptiveTrackSelection(group, tracks, bandwidthMeter, maxInitialBitrate,
|
||||
minDurationForQualityIncreaseMs, maxDurationForQualityDecreaseMs,
|
||||
minDurationToRetainAfterDiscardMs, bandwidthFraction);
|
||||
minDurationToRetainAfterDiscardMs, bandwidthFraction,
|
||||
bufferedFractionToLiveEdgeForQualityIncrease);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -92,6 +129,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
public static final int DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS = 25000;
|
||||
public static final int DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS = 25000;
|
||||
public static final float DEFAULT_BANDWIDTH_FRACTION = 0.75f;
|
||||
public static final float DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE = 0.75f;
|
||||
|
||||
private final BandwidthMeter bandwidthMeter;
|
||||
private final int maxInitialBitrate;
|
||||
|
|
@ -99,6 +137,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
private final long maxDurationForQualityDecreaseUs;
|
||||
private final long minDurationToRetainAfterDiscardUs;
|
||||
private final float bandwidthFraction;
|
||||
private final float bufferedFractionToLiveEdgeForQualityIncrease;
|
||||
|
||||
private int selectedIndex;
|
||||
private int reason;
|
||||
|
|
@ -114,7 +153,9 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
this (group, tracks, bandwidthMeter, DEFAULT_MAX_INITIAL_BITRATE,
|
||||
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_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
|
||||
DEFAULT_BANDWIDTH_FRACTION,
|
||||
DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -135,11 +176,17 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
* @param bandwidthFraction The fraction of the available bandwidth that the selection should
|
||||
* consider available for use. Setting to a value less than 1 is recommended to account
|
||||
* for inaccuracies in the bandwidth estimator.
|
||||
* @param bufferedFractionToLiveEdgeForQualityIncrease For live streaming, the fraction of
|
||||
* the duration from current playback position to the live edge that has to be buffered
|
||||
* before the selected track can be switched to one of higher quality. This parameter is
|
||||
* only applied when the playback position is closer to the live edge than
|
||||
* {@code minDurationForQualityIncreaseMs}, which would otherwise prevent switching to a
|
||||
* higher quality from happening.
|
||||
*/
|
||||
public AdaptiveTrackSelection(TrackGroup group, int[] tracks, BandwidthMeter bandwidthMeter,
|
||||
int maxInitialBitrate, long minDurationForQualityIncreaseMs,
|
||||
long maxDurationForQualityDecreaseMs, long minDurationToRetainAfterDiscardMs,
|
||||
float bandwidthFraction) {
|
||||
float bandwidthFraction, float bufferedFractionToLiveEdgeForQualityIncrease) {
|
||||
super(group, tracks);
|
||||
this.bandwidthMeter = bandwidthMeter;
|
||||
this.maxInitialBitrate = maxInitialBitrate;
|
||||
|
|
@ -147,12 +194,14 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
this.maxDurationForQualityDecreaseUs = maxDurationForQualityDecreaseMs * 1000L;
|
||||
this.minDurationToRetainAfterDiscardUs = minDurationToRetainAfterDiscardMs * 1000L;
|
||||
this.bandwidthFraction = bandwidthFraction;
|
||||
this.bufferedFractionToLiveEdgeForQualityIncrease =
|
||||
bufferedFractionToLiveEdgeForQualityIncrease;
|
||||
selectedIndex = determineIdealSelectedIndex(Long.MIN_VALUE);
|
||||
reason = C.SELECTION_REASON_INITIAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSelectedTrack(long bufferedDurationUs) {
|
||||
public void updateSelectedTrack(long bufferedDurationUs, long availableDurationUs) {
|
||||
long nowMs = SystemClock.elapsedRealtime();
|
||||
// Stash the current selection, then make a new one.
|
||||
int currentSelectedIndex = selectedIndex;
|
||||
|
|
@ -160,12 +209,13 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
if (selectedIndex == currentSelectedIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isBlacklisted(currentSelectedIndex, nowMs)) {
|
||||
// Revert back to the current selection if conditions are not suitable for switching.
|
||||
Format currentFormat = getFormat(currentSelectedIndex);
|
||||
Format selectedFormat = getFormat(selectedIndex);
|
||||
if (selectedFormat.bitrate > currentFormat.bitrate
|
||||
&& bufferedDurationUs < minDurationForQualityIncreaseUs) {
|
||||
&& bufferedDurationUs < minDurationForQualityIncreaseUs(availableDurationUs)) {
|
||||
// The selected track is a higher quality, but we have insufficient buffer to safely switch
|
||||
// up. Defer switching up for now.
|
||||
selectedIndex = currentSelectedIndex;
|
||||
|
|
@ -251,4 +301,12 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||
return lowestBitrateNonBlacklistedIndex;
|
||||
}
|
||||
|
||||
private long minDurationForQualityIncreaseUs(long availableDurationUs) {
|
||||
boolean isAvailableDurationTooShort = availableDurationUs != C.TIME_UNSET
|
||||
&& availableDurationUs <= minDurationForQualityIncreaseUs;
|
||||
return isAvailableDurationTooShort
|
||||
? (long) (availableDurationUs * bufferedFractionToLiveEdgeForQualityIncrease)
|
||||
: minDurationForQualityIncreaseUs;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public final class FixedTrackSelection extends BaseTrackSelection {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateSelectedTrack(long bufferedDurationUs) {
|
||||
public void updateSelectedTrack(long bufferedDurationUs, long availableDurationUs) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ public final class RandomTrackSelection extends BaseTrackSelection {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateSelectedTrack(long bufferedDurationUs) {
|
||||
public void updateSelectedTrack(long bufferedDurationUs, long availableDurationUs) {
|
||||
// Count the number of non-blacklisted formats.
|
||||
long nowMs = SystemClock.elapsedRealtime();
|
||||
int nonBlacklistedFormatCount = 0;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import java.util.List;
|
|||
* {@link TrackGroup}, and a possibly varying individual selected track from the subset.
|
||||
* <p>
|
||||
* Tracks belonging to the subset are exposed in decreasing bandwidth order. The individual selected
|
||||
* track may change as a result of calling {@link #updateSelectedTrack(long)}.
|
||||
* track may change as a result of calling {@link #updateSelectedTrack(long, long)}.
|
||||
*/
|
||||
public interface TrackSelection {
|
||||
|
||||
|
|
@ -126,8 +126,11 @@ public interface TrackSelection {
|
|||
* Updates the selected track.
|
||||
*
|
||||
* @param bufferedDurationUs The duration of media currently buffered in microseconds.
|
||||
* @param availableDurationUs The duration of media available for buffering from the current
|
||||
* playback position, in microseconds, or {@link C#TIME_UNSET} if media can be buffered
|
||||
* to the end of the current period.
|
||||
*/
|
||||
void updateSelectedTrack(long bufferedDurationUs);
|
||||
void updateSelectedTrack(long bufferedDurationUs, long availableDurationUs);
|
||||
|
||||
/**
|
||||
* May be called periodically by sources that load media in discrete {@link MediaChunk}s and
|
||||
|
|
@ -148,10 +151,10 @@ public interface TrackSelection {
|
|||
|
||||
/**
|
||||
* Attempts to blacklist the track at the specified index in the selection, making it ineligible
|
||||
* for selection by calls to {@link #updateSelectedTrack(long)} for the specified period of time.
|
||||
* Blacklisting will fail if all other tracks are currently blacklisted. If blacklisting the
|
||||
* for selection by calls to {@link #updateSelectedTrack(long, long)} for the specified period of
|
||||
* time. Blacklisting will fail if all other tracks are currently blacklisted. If blacklisting the
|
||||
* currently selected track, note that it will remain selected until the next call to
|
||||
* {@link #updateSelectedTrack(long)}.
|
||||
* {@link #updateSelectedTrack(long, long)}.
|
||||
*
|
||||
* @param index The index of the track in the selection.
|
||||
* @param blacklistDurationMs The duration of time for which the track should be blacklisted, in
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
private int periodIndex;
|
||||
private IOException fatalError;
|
||||
private boolean missingLastSegment;
|
||||
private long liveEdgeTimeUs;
|
||||
|
||||
/**
|
||||
* @param manifestLoaderErrorThrower Throws errors affecting loading of manifests.
|
||||
|
|
@ -130,6 +131,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
this.maxSegmentsPerLoad = maxSegmentsPerLoad;
|
||||
|
||||
long periodDurationUs = manifest.getPeriodDurationUs(periodIndex);
|
||||
liveEdgeTimeUs = C.TIME_UNSET;
|
||||
List<Representation> representations = getRepresentations();
|
||||
representationHolders = new RepresentationHolder[trackSelection.length()];
|
||||
for (int i = 0; i < representationHolders.length; i++) {
|
||||
|
|
@ -179,7 +181,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
}
|
||||
|
||||
long bufferedDurationUs = previous != null ? (previous.endTimeUs - playbackPositionUs) : 0;
|
||||
trackSelection.updateSelectedTrack(bufferedDurationUs);
|
||||
long timeToLiveEdgeUs = resolveTimeToLiveEdgeUs(playbackPositionUs, previous == null);
|
||||
trackSelection.updateSelectedTrack(bufferedDurationUs, timeToLiveEdgeUs);
|
||||
|
||||
RepresentationHolder representationHolder =
|
||||
representationHolders[trackSelection.getSelectedIndex()];
|
||||
|
|
@ -203,7 +206,6 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
}
|
||||
}
|
||||
|
||||
long nowUs = getNowUnixTimeUs();
|
||||
int availableSegmentCount = representationHolder.getSegmentCount();
|
||||
if (availableSegmentCount == 0) {
|
||||
// The index doesn't define any segments.
|
||||
|
|
@ -216,21 +218,23 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
if (availableSegmentCount == DashSegmentIndex.INDEX_UNBOUNDED) {
|
||||
// The index is itself unbounded. We need to use the current time to calculate the range of
|
||||
// available segments.
|
||||
long liveEdgeTimeUs = nowUs - manifest.availabilityStartTime * 1000;
|
||||
long periodStartUs = manifest.getPeriod(periodIndex).startMs * 1000;
|
||||
long liveEdgeTimeUs = getNowUnixTimeUs() - C.msToUs(manifest.availabilityStartTime);
|
||||
long periodStartUs = C.msToUs(manifest.getPeriod(periodIndex).startMs);
|
||||
long liveEdgeTimeInPeriodUs = liveEdgeTimeUs - periodStartUs;
|
||||
if (manifest.timeShiftBufferDepth != C.TIME_UNSET) {
|
||||
long bufferDepthUs = manifest.timeShiftBufferDepth * 1000;
|
||||
long bufferDepthUs = C.msToUs(manifest.timeShiftBufferDepth);
|
||||
firstAvailableSegmentNum = Math.max(firstAvailableSegmentNum,
|
||||
representationHolder.getSegmentNum(liveEdgeTimeInPeriodUs - bufferDepthUs));
|
||||
}
|
||||
// getSegmentNum(liveEdgeTimestampUs) will not be completed yet, so subtract one to get the
|
||||
// getSegmentNum(liveEdgeTimeInPeriodUs) will not be completed yet, so subtract one to get the
|
||||
// index of the last completed segment.
|
||||
lastAvailableSegmentNum = representationHolder.getSegmentNum(liveEdgeTimeInPeriodUs) - 1;
|
||||
} else {
|
||||
lastAvailableSegmentNum = firstAvailableSegmentNum + availableSegmentCount - 1;
|
||||
}
|
||||
|
||||
updateLiveEdgeTimeUs(representationHolder, lastAvailableSegmentNum);
|
||||
|
||||
int segmentNum;
|
||||
if (previous == null) {
|
||||
segmentNum = Util.constrainValue(representationHolder.getSegmentNum(playbackPositionUs),
|
||||
|
|
@ -311,6 +315,19 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
return representations;
|
||||
}
|
||||
|
||||
private void updateLiveEdgeTimeUs(RepresentationHolder representationHolder,
|
||||
int lastAvailableSegmentNum) {
|
||||
if (manifest.dynamic) {
|
||||
DashSegmentIndex segmentIndex = representationHolder.representation.getIndex();
|
||||
long lastSegmentDurationUs = segmentIndex.getDurationUs(lastAvailableSegmentNum,
|
||||
manifest.getPeriodDurationUs(periodIndex));
|
||||
liveEdgeTimeUs = segmentIndex.getTimeUs(lastAvailableSegmentNum)
|
||||
+ lastSegmentDurationUs;
|
||||
} else {
|
||||
liveEdgeTimeUs = C.TIME_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
private long getNowUnixTimeUs() {
|
||||
if (elapsedRealtimeOffsetMs != 0) {
|
||||
return (SystemClock.elapsedRealtime() + elapsedRealtimeOffsetMs) * 1000;
|
||||
|
|
@ -375,6 +392,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
}
|
||||
}
|
||||
|
||||
private long resolveTimeToLiveEdgeUs(long playbackPositionUs, boolean isAfterPositionReset) {
|
||||
boolean resolveTimeToLiveEdgePossible = manifest.dynamic
|
||||
&& !isAfterPositionReset && liveEdgeTimeUs != C.TIME_UNSET;
|
||||
return resolveTimeToLiveEdgePossible ? liveEdgeTimeUs - playbackPositionUs : C.TIME_UNSET;
|
||||
}
|
||||
|
||||
// Protected classes.
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ import java.util.List;
|
|||
// the way in which HlsSampleStreamWrapper generates track groups. Use only index based methods
|
||||
// in TrackSelection to avoid unexpected behavior.
|
||||
private TrackSelection trackSelection;
|
||||
private long liveEdgeTimeUs;
|
||||
|
||||
/**
|
||||
* @param playlistTracker The {@link HlsPlaylistTracker} from which to obtain media playlists.
|
||||
|
|
@ -122,6 +123,7 @@ import java.util.List;
|
|||
this.variants = variants;
|
||||
this.timestampAdjusterProvider = timestampAdjusterProvider;
|
||||
this.muxedCaptionFormats = muxedCaptionFormats;
|
||||
liveEdgeTimeUs = C.TIME_UNSET;
|
||||
Format[] variantFormats = new Format[variants.length];
|
||||
int[] initialTrackSelection = new int[variants.length];
|
||||
for (int i = 0; i < variants.length; i++) {
|
||||
|
|
@ -214,7 +216,8 @@ import java.util.List;
|
|||
(independentSegments ? previous.endTimeUs : previous.startTimeUs) - playbackPositionUs);
|
||||
|
||||
// Select the variant.
|
||||
trackSelection.updateSelectedTrack(bufferedDurationUs);
|
||||
long timeToLiveEdgeUs = resolveTimeToLiveEdgeUs(playbackPositionUs, previous == null);
|
||||
trackSelection.updateSelectedTrack(bufferedDurationUs, timeToLiveEdgeUs);
|
||||
int selectedVariantIndex = trackSelection.getSelectedIndexInTrackGroup();
|
||||
|
||||
boolean switchingVariant = oldVariantIndex != selectedVariantIndex;
|
||||
|
|
@ -228,6 +231,8 @@ import java.util.List;
|
|||
HlsMediaPlaylist mediaPlaylist = playlistTracker.getPlaylistSnapshot(selectedUrl);
|
||||
independentSegments = mediaPlaylist.hasIndependentSegmentsTag;
|
||||
|
||||
updateLiveEdgeTimeUs(mediaPlaylist);
|
||||
|
||||
// Select the chunk.
|
||||
int chunkMediaSequence;
|
||||
if (previous == null || switchingVariant) {
|
||||
|
|
@ -360,6 +365,16 @@ import java.util.List;
|
|||
|
||||
// Private methods.
|
||||
|
||||
private long resolveTimeToLiveEdgeUs(long playbackPositionUs, boolean isAfterPositionReset) {
|
||||
final boolean resolveTimeToLiveEdgePossible = !isAfterPositionReset
|
||||
&& liveEdgeTimeUs != C.TIME_UNSET;
|
||||
return resolveTimeToLiveEdgePossible ? liveEdgeTimeUs - playbackPositionUs : C.TIME_UNSET;
|
||||
}
|
||||
|
||||
private void updateLiveEdgeTimeUs(HlsMediaPlaylist mediaPlaylist) {
|
||||
liveEdgeTimeUs = mediaPlaylist.hasEndTag ? C.TIME_UNSET : mediaPlaylist.getEndTimeUs();
|
||||
}
|
||||
|
||||
private EncryptionKeyChunk newEncryptionKeyChunk(Uri keyUri, String iv, int variantIndex,
|
||||
int trackSelectionReason, Object trackSelectionData) {
|
||||
DataSpec dataSpec = new DataSpec(keyUri, 0, C.LENGTH_UNSET, null, DataSpec.FLAG_ALLOW_GZIP);
|
||||
|
|
@ -409,7 +424,7 @@ import java.util.List;
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateSelectedTrack(long bufferedDurationUs) {
|
||||
public void updateSelectedTrack(long bufferedDurationUs, long availableDurationUs) {
|
||||
long nowMs = SystemClock.elapsedRealtime();
|
||||
if (!isBlacklisted(selectedIndex, nowMs)) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ import java.util.LinkedList;
|
|||
private boolean prepared;
|
||||
private int enabledTrackCount;
|
||||
private Format downstreamTrackFormat;
|
||||
private int upstreamChunkUid;
|
||||
private boolean released;
|
||||
|
||||
// Tracks are complicated in HLS. See documentation of buildTracks for details.
|
||||
|
|
@ -255,7 +254,7 @@ import java.util.LinkedList;
|
|||
// may need to be discarded.
|
||||
boolean primarySampleQueueDirty = false;
|
||||
if (!seenFirstTrackSelection) {
|
||||
primaryTrackSelection.updateSelectedTrack(0);
|
||||
primaryTrackSelection.updateSelectedTrack(0, C.TIME_UNSET);
|
||||
int chunkIndex = chunkSource.getTrackGroup().indexOf(mediaChunks.getLast().trackFormat);
|
||||
if (primaryTrackSelection.getSelectedIndexInTrackGroup() != chunkIndex) {
|
||||
// This is the first selection and the chunk loaded during preparation does not match
|
||||
|
|
@ -553,7 +552,6 @@ import java.util.LinkedList;
|
|||
* samples already queued to the wrapper.
|
||||
*/
|
||||
public void init(int chunkUid, boolean shouldSpliceIn) {
|
||||
upstreamChunkUid = chunkUid;
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
sampleQueue.sourceId(chunkUid);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,7 +155,8 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
|||
}
|
||||
|
||||
long bufferedDurationUs = previous != null ? (previous.endTimeUs - playbackPositionUs) : 0;
|
||||
trackSelection.updateSelectedTrack(bufferedDurationUs);
|
||||
long timeToLiveEdgeUs = resolveTimeToLiveEdgeUs(playbackPositionUs);
|
||||
trackSelection.updateSelectedTrack(bufferedDurationUs, timeToLiveEdgeUs);
|
||||
|
||||
StreamElement streamElement = manifest.streamElements[elementIndex];
|
||||
if (streamElement.chunkCount == 0) {
|
||||
|
|
@ -222,4 +223,20 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
|||
extractorWrapper);
|
||||
}
|
||||
|
||||
private long resolveTimeToLiveEdgeUs(long playbackPositionUs) {
|
||||
if (!manifest.isLive) {
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
|
||||
StreamElement currentElement = manifest.streamElements[elementIndex];
|
||||
if (currentElement.chunkCount == 0) {
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
|
||||
int lastChunkIndex = currentElement.chunkCount - 1;
|
||||
long lastChunkEndTimeUs = currentElement.getStartTimeUs(lastChunkIndex)
|
||||
+ currentElement.getChunkDurationUs(lastChunkIndex);
|
||||
return lastChunkEndTimeUs - playbackPositionUs;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package com.google.android.exoplayer2.testutil;
|
||||
|
||||
import android.net.Uri;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.source.chunk.Chunk;
|
||||
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
|
||||
|
|
@ -83,7 +84,7 @@ public final class FakeChunkSource implements ChunkSource {
|
|||
@Override
|
||||
public void getNextChunk(MediaChunk previous, long playbackPositionUs, ChunkHolder out) {
|
||||
long bufferedDurationUs = previous != null ? (previous.endTimeUs - playbackPositionUs) : 0;
|
||||
trackSelection.updateSelectedTrack(bufferedDurationUs);
|
||||
trackSelection.updateSelectedTrack(bufferedDurationUs, C.TIME_UNSET);
|
||||
int chunkIndex = previous == null ? dataSet.getChunkIndexByPosition(playbackPositionUs)
|
||||
: previous.getNextChunkIndex();
|
||||
if (chunkIndex >= dataSet.getChunkCount()) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue