Commit db33b3bb by aquilescanta Committed by Oliver Woodman

Allow HlsSampleStreamWrapper to cancel partial media chunk loads

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=213439145
parent decbb334
...@@ -223,9 +223,6 @@ import java.util.List; ...@@ -223,9 +223,6 @@ import java.util.List;
* started, the value will be the starting position in the period minus the duration of any * started, the value will be the starting position in the period minus the duration of any
* media in previous periods still to be played. * media in previous periods still to be played.
* @param loadPositionUs The current load position relative to the period start in microseconds. * @param loadPositionUs The current load position relative to the period start in microseconds.
* If {@code queue} is empty, this is the starting position from which chunks should be
* provided. Else it's equal to {@link HlsMediaChunk#endTimeUs} of the last chunk in the
* {@code queue}.
* @param queue The queue of buffered {@link HlsMediaChunk}s. * @param queue The queue of buffered {@link HlsMediaChunk}s.
* @param out A holder to populate. * @param out A holder to populate.
*/ */
...@@ -237,12 +234,12 @@ import java.util.List; ...@@ -237,12 +234,12 @@ import java.util.List;
long bufferedDurationUs = loadPositionUs - playbackPositionUs; long bufferedDurationUs = loadPositionUs - playbackPositionUs;
long timeToLiveEdgeUs = resolveTimeToLiveEdgeUs(playbackPositionUs); long timeToLiveEdgeUs = resolveTimeToLiveEdgeUs(playbackPositionUs);
if (previous != null && !independentSegments) { if (previous != null && !independentSegments) {
// Unless segments are known to be independent, switching variant will require downloading // Unless segments are known to be independent, switching variant requires downloading
// overlapping segments. Hence we will subtract previous chunk's duration from buffered // overlapping segments. Hence we subtract the previous segment's duration from the buffered
// duration. // duration.
// This may affect the live-streaming adaptive track selection logic, when we are comparing // This may affect the live-streaming adaptive track selection logic, when we compare the
// buffered duration to time to live edge to decide whether to switch. Therefore, // buffered duration to time-to-live-edge to decide whether to switch. Therefore, we subtract
// we will subtract this same amount from timeToLiveEdgeUs as well. // the duration of the last loaded segment from timeToLiveEdgeUs as well.
long subtractedDurationUs = previous.getDurationUs(); long subtractedDurationUs = previous.getDurationUs();
bufferedDurationUs = Math.max(0, bufferedDurationUs - subtractedDurationUs); bufferedDurationUs = Math.max(0, bufferedDurationUs - subtractedDurationUs);
if (timeToLiveEdgeUs != C.TIME_UNSET) { if (timeToLiveEdgeUs != C.TIME_UNSET) {
...@@ -420,7 +417,7 @@ import java.util.List; ...@@ -420,7 +417,7 @@ import java.util.List;
} }
/** /**
* Returns list of {@link MediaChunkIterator}s for upcoming media chunks. * Returns an array of {@link MediaChunkIterator}s for upcoming media chunks.
* *
* @param previous The previous media chunk. May be null. * @param previous The previous media chunk. May be null.
* @param loadPositionUs The position at which the iterators will start. * @param loadPositionUs The position at which the iterators will start.
...@@ -458,6 +455,18 @@ import java.util.List; ...@@ -458,6 +455,18 @@ import java.util.List;
// Private methods. // Private methods.
/**
* Returns the media sequence number of the segment to load next in {@code mediaPlaylist}.
*
* @param previous The last (at least partially) loaded segment.
* @param switchingVariant Whether the segment to load is not preceded by a segment in the same
* variant.
* @param mediaPlaylist The media playlist to which the segment to load belongs.
* @param startOfPlaylistInPeriodUs The start of {@code mediaPlaylist} relative to the period
* start in microseconds.
* @param loadPositionUs The current load position relative to the period start in microseconds.
* @return The media sequence of the segment to load.
*/
private long getChunkMediaSequence( private long getChunkMediaSequence(
@Nullable HlsMediaChunk previous, @Nullable HlsMediaChunk previous,
boolean switchingVariant, boolean switchingVariant,
...@@ -480,6 +489,8 @@ import java.util.List; ...@@ -480,6 +489,8 @@ import java.util.List;
/* stayInBounds= */ !playlistTracker.isLive() || previous == null) /* stayInBounds= */ !playlistTracker.isLive() || previous == null)
+ mediaPlaylist.mediaSequence; + mediaPlaylist.mediaSequence;
} }
// We ignore the case of previous not having loaded completely, in which case we load the next
// segment.
return previous.getNextChunkIndex(); return previous.getNextChunkIndex();
} }
......
...@@ -152,7 +152,7 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -152,7 +152,7 @@ import java.util.concurrent.atomic.AtomicInteger;
if (previousChunk != null) { if (previousChunk != null) {
id3Decoder = previousChunk.id3Decoder; id3Decoder = previousChunk.id3Decoder;
id3Data = previousChunk.id3Data; id3Data = previousChunk.id3Data;
shouldSpliceIn = previousChunk.hlsUrl != hlsUrl; shouldSpliceIn = previousChunk.hlsUrl != hlsUrl || !previousChunk.loadCompleted;
previousExtractor = previousChunk.discontinuitySequenceNumber != discontinuitySequenceNumber previousExtractor = previousChunk.discontinuitySequenceNumber != discontinuitySequenceNumber
|| shouldSpliceIn ? null : previousChunk.extractor; || shouldSpliceIn ? null : previousChunk.extractor;
} else { } else {
......
...@@ -460,9 +460,7 @@ import java.util.List; ...@@ -460,9 +460,7 @@ import java.util.List;
&& finishedReadingChunk(mediaChunks.get(discardToMediaChunkIndex))) { && finishedReadingChunk(mediaChunks.get(discardToMediaChunkIndex))) {
discardToMediaChunkIndex++; discardToMediaChunkIndex++;
} }
if (discardToMediaChunkIndex > 0) {
Util.removeRange(mediaChunks, 0, discardToMediaChunkIndex); Util.removeRange(mediaChunks, 0, discardToMediaChunkIndex);
}
HlsMediaChunk currentChunk = mediaChunks.get(0); HlsMediaChunk currentChunk = mediaChunks.get(0);
Format trackFormat = currentChunk.trackFormat; Format trackFormat = currentChunk.trackFormat;
if (!trackFormat.equals(downstreamTrackFormat)) { if (!trackFormat.equals(downstreamTrackFormat)) {
...@@ -554,7 +552,11 @@ import java.util.List; ...@@ -554,7 +552,11 @@ import java.util.List;
loadPositionUs = pendingResetPositionUs; loadPositionUs = pendingResetPositionUs;
} else { } else {
chunkQueue = readOnlyMediaChunks; chunkQueue = readOnlyMediaChunks;
loadPositionUs = getLastMediaChunk().endTimeUs; HlsMediaChunk lastMediaChunk = getLastMediaChunk();
loadPositionUs =
lastMediaChunk.isLoadCompleted()
? lastMediaChunk.endTimeUs
: Math.max(lastSeekPositionUs, lastMediaChunk.startTimeUs);
} }
chunkSource.getNextChunk(positionUs, loadPositionUs, chunkQueue, nextChunkHolder); chunkSource.getNextChunk(positionUs, loadPositionUs, chunkQueue, nextChunkHolder);
boolean endOfStream = nextChunkHolder.endOfStream; boolean endOfStream = nextChunkHolder.endOfStream;
...@@ -666,17 +668,15 @@ import java.util.List; ...@@ -666,17 +668,15 @@ import java.util.List;
boolean blacklistSucceeded = false; boolean blacklistSucceeded = false;
LoadErrorAction loadErrorAction; LoadErrorAction loadErrorAction;
if (!isMediaChunk || bytesLoaded == 0) {
long blacklistDurationMs = long blacklistDurationMs =
loadErrorHandlingPolicy.getBlacklistDurationMsFor( loadErrorHandlingPolicy.getBlacklistDurationMsFor(
loadable.type, loadDurationMs, error, errorCount); loadable.type, loadDurationMs, error, errorCount);
if (blacklistDurationMs != C.TIME_UNSET) { if (blacklistDurationMs != C.TIME_UNSET) {
blacklistSucceeded = chunkSource.maybeBlacklistTrack(loadable, blacklistDurationMs); blacklistSucceeded = chunkSource.maybeBlacklistTrack(loadable, blacklistDurationMs);
} }
}
if (blacklistSucceeded) { if (blacklistSucceeded) {
if (isMediaChunk) { if (isMediaChunk && bytesLoaded == 0) {
HlsMediaChunk removed = mediaChunks.remove(mediaChunks.size() - 1); HlsMediaChunk removed = mediaChunks.remove(mediaChunks.size() - 1);
Assertions.checkState(removed == loadable); Assertions.checkState(removed == loadable);
if (mediaChunks.isEmpty()) { if (mediaChunks.isEmpty()) {
...@@ -707,7 +707,7 @@ import java.util.List; ...@@ -707,7 +707,7 @@ import java.util.List;
loadable.endTimeUs, loadable.endTimeUs,
elapsedRealtimeMs, elapsedRealtimeMs,
loadDurationMs, loadDurationMs,
loadable.bytesLoaded(), bytesLoaded,
error, error,
/* wasCanceled= */ !loadErrorAction.isRetry()); /* wasCanceled= */ !loadErrorAction.isRetry());
......
...@@ -33,6 +33,7 @@ android { ...@@ -33,6 +33,7 @@ android {
dependencies { dependencies {
androidTestImplementation 'androidx.test:rules:' + testRunnerVersion androidTestImplementation 'androidx.test:rules:' + testRunnerVersion
androidTestImplementation 'androidx.test:runner:' + testRunnerVersion androidTestImplementation 'androidx.test:runner:' + testRunnerVersion
androidTestImplementation 'com.android.support:support-annotations:' + supportLibraryVersion
androidTestImplementation project(modulePrefix + 'library-core') androidTestImplementation project(modulePrefix + 'library-core')
androidTestImplementation project(modulePrefix + 'library-dash') androidTestImplementation project(modulePrefix + 'library-dash')
androidTestImplementation project(modulePrefix + 'library-hls') androidTestImplementation project(modulePrefix + 'library-hls')
......
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