Commit 078c5741 by olly Committed by Oliver Woodman

Correctly reset the upstream format from SampleQueues in HLS

When resetting a SampleQueue, by default the upstream format
is not cleared. This is necessary for progressive playbacks,
since (a) the formats never change, and (b) the extractors
only output them once. So when a seek occurs, it's necessary
to clear all sample data from the queue, but retain the current
upstream format.

Uniquely for HLS, the media in a SampleQueue that we may read
from can be in a format not supported by the consuming renderers.
We clear all the sample data from the queue in this case, but
not the upstream format. Since we have an optimization that
allows the upstream format to be read in advance of another
sample being written into the queue, this can result in an
unsupported format being read by a consuming renderer. This
change ensures the upstream format is correctly cleared in the
problematic case.

Issue: #3079

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=162360267
parent b950a5e8
......@@ -77,7 +77,15 @@ import com.google.android.exoplayer2.util.Util;
upstreamKeyframeRequired = true;
}
public void clearSampleData() {
/**
* Clears all sample metadata from the queue.
*
* @param resetUpstreamFormat Whether the upstream format should be cleared. If set to false,
* samples queued after the reset (and before a subsequent call to {@link #format(Format)})
* are assumed to have the current upstream format. If set to true, {@link #format(Format)}
* must be called after the reset before any more samples can be queued.
*/
public void reset(boolean resetUpstreamFormat) {
length = 0;
absoluteStartIndex = 0;
relativeStartIndex = 0;
......@@ -85,6 +93,10 @@ import com.google.android.exoplayer2.util.Util;
upstreamKeyframeRequired = true;
largestDiscardedTimestampUs = Long.MIN_VALUE;
largestQueuedTimestampUs = Long.MIN_VALUE;
if (resetUpstreamFormat) {
upstreamFormat = null;
upstreamFormatRequired = true;
}
}
/**
......@@ -154,7 +166,7 @@ import com.google.android.exoplayer2.util.Util;
/**
* Returns the largest sample timestamp that has been queued since the last call to
* {@link #resetLargestParsedTimestamps()}.
* {@link #reset(boolean)}.
* <p>
* Samples that were discarded by calling {@link #discardUpstreamSamples(int)} are not
* considered as having been queued. Samples that were dequeued from the front of the queue are
......
......@@ -90,10 +90,22 @@ public final class SampleQueue implements TrackOutput {
// Called by the consuming thread, but only when there is no loading thread.
/**
* Resets the output.
* Resets the output without clearing the upstream format. Equivalent to {@code reset(false)}.
*/
public void reset() {
metadataQueue.clearSampleData();
reset(false);
}
/**
* Resets the output.
*
* @param resetUpstreamFormat Whether the upstream format should be cleared. If set to false,
* samples queued after the reset (and before a subsequent call to {@link #format(Format)})
* are assumed to have the current upstream format. If set to true, {@link #format(Format)}
* must be called after the reset before any more samples can be queued.
*/
public void reset(boolean resetUpstreamFormat) {
metadataQueue.reset(resetUpstreamFormat);
clearAllocationNodes(firstAllocationNode);
firstAllocationNode = new AllocationNode(0, allocationLength);
readAllocationNode = firstAllocationNode;
......
......@@ -105,6 +105,7 @@ import java.util.LinkedList;
private long lastSeekPositionUs;
private long pendingResetPositionUs;
private boolean pendingResetUpstreamFormats;
private boolean seenFirstTrackSelection;
private boolean loadingFinished;
......@@ -233,6 +234,7 @@ import java.util.LinkedList;
if (enabledTrackCount == 0) {
chunkSource.reset();
downstreamTrackFormat = null;
pendingResetUpstreamFormats |= !seenFirstTrackSelection;
mediaChunks.clear();
if (loader.isLoading()) {
// Discard as much as we can synchronously.
......@@ -241,20 +243,20 @@ import java.util.LinkedList;
}
loader.cancelLoading();
} else {
for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.reset();
}
resetSampleQueues();
}
} else {
if (!forceReset && !seenFirstTrackSelection && primaryTrackSelection != null
&& !mediaChunks.isEmpty()) {
if (!seenFirstTrackSelection && primaryTrackSelection != null && !mediaChunks.isEmpty()) {
primaryTrackSelection.updateSelectedTrack(0);
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 the
// selection. We need to reset to discard it.
// selection. We need to reset to discard it. We also need to ensure that the upstream
// formats are cleared from the sample queues so that they cannot be read. This is
// necessary because the consuming renderers may not support these formats.
forceReset = true;
seekRequired = true;
pendingResetUpstreamFormats = true;
}
}
if (seekRequired) {
......@@ -300,9 +302,7 @@ import java.util.LinkedList;
if (loader.isLoading()) {
loader.cancelLoading();
} else {
for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.reset();
}
resetSampleQueues();
}
return true;
}
......@@ -343,9 +343,7 @@ import java.util.LinkedList;
@Override
public void onLoaderReleased() {
for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.reset();
}
resetSampleQueues();
}
public void setIsTimestampMaster(boolean isTimestampMaster) {
......@@ -410,6 +408,13 @@ import java.util.LinkedList;
return true;
}
private void resetSampleQueues() {
for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.reset(pendingResetUpstreamFormats);
}
pendingResetUpstreamFormats = false;
}
// SequenceableLoader implementation
@Override
......@@ -483,9 +488,7 @@ import java.util.LinkedList;
loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs,
loadable.endTimeUs, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded());
if (!released) {
for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.reset();
}
resetSampleQueues();
if (enabledTrackCount > 0) {
callback.onContinueLoadingRequested(this);
}
......
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