Fix spurious reset of PreparedState boolean flags
PiperOrigin-RevId: 300513930
This commit is contained in:
parent
c85e5137f0
commit
16e6ea6e40
@ -57,6 +57,7 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/** A {@link MediaPeriod} that extracts data using an {@link Extractor}. */
|
/** A {@link MediaPeriod} that extracts data using an {@link Extractor}. */
|
||||||
@ -111,23 +112,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
|
|
||||||
@Nullable private Callback callback;
|
@Nullable private Callback callback;
|
||||||
@Nullable private SeekMap seekMap;
|
|
||||||
@Nullable private IcyHeaders icyHeaders;
|
@Nullable private IcyHeaders icyHeaders;
|
||||||
private SampleQueue[] sampleQueues;
|
private SampleQueue[] sampleQueues;
|
||||||
private TrackId[] sampleQueueTrackIds;
|
private TrackId[] sampleQueueTrackIds;
|
||||||
private boolean sampleQueuesBuilt;
|
private boolean sampleQueuesBuilt;
|
||||||
|
|
||||||
private @MonotonicNonNull PreparedState preparedState;
|
private boolean prepared;
|
||||||
private boolean haveAudioVideoTracks;
|
private boolean haveAudioVideoTracks;
|
||||||
|
private @MonotonicNonNull TrackState trackState;
|
||||||
|
private @MonotonicNonNull SeekMap seekMap;
|
||||||
|
private long durationUs;
|
||||||
|
private boolean isLive;
|
||||||
private int dataType;
|
private int dataType;
|
||||||
|
|
||||||
private boolean seenFirstTrackSelection;
|
private boolean seenFirstTrackSelection;
|
||||||
private boolean notifyDiscontinuity;
|
private boolean notifyDiscontinuity;
|
||||||
private boolean notifiedReadingStarted;
|
private boolean notifiedReadingStarted;
|
||||||
private int enabledTrackCount;
|
private int enabledTrackCount;
|
||||||
private long durationUs;
|
|
||||||
private long length;
|
private long length;
|
||||||
private boolean isLive;
|
|
||||||
|
|
||||||
private long lastSeekPositionUs;
|
private long lastSeekPositionUs;
|
||||||
private long pendingResetPositionUs;
|
private long pendingResetPositionUs;
|
||||||
@ -197,7 +199,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void release() {
|
public void release() {
|
||||||
if (preparedState != null) {
|
if (prepared) {
|
||||||
// Discard as much as we can synchronously. We only do this if we're prepared, since otherwise
|
// Discard as much as we can synchronously. We only do this if we're prepared, since otherwise
|
||||||
// sampleQueues may still be being modified by the loading thread.
|
// sampleQueues may still be being modified by the loading thread.
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
@ -229,14 +231,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@Override
|
@Override
|
||||||
public void maybeThrowPrepareError() throws IOException {
|
public void maybeThrowPrepareError() throws IOException {
|
||||||
maybeThrowError();
|
maybeThrowError();
|
||||||
if (loadingFinished && preparedState == null) {
|
if (loadingFinished && !prepared) {
|
||||||
throw new ParserException("Loading finished before preparation is complete.");
|
throw new ParserException("Loading finished before preparation is complete.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackGroupArray getTrackGroups() {
|
public TrackGroupArray getTrackGroups() {
|
||||||
return Assertions.checkNotNull(preparedState).tracks;
|
assertPrepared();
|
||||||
|
return trackState.tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -246,8 +249,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@NullableType SampleStream[] streams,
|
@NullableType SampleStream[] streams,
|
||||||
boolean[] streamResetFlags,
|
boolean[] streamResetFlags,
|
||||||
long positionUs) {
|
long positionUs) {
|
||||||
TrackGroupArray tracks = Assertions.checkNotNull(preparedState).tracks;
|
assertPrepared();
|
||||||
boolean[] trackEnabledStates = preparedState.trackEnabledStates;
|
TrackGroupArray tracks = trackState.tracks;
|
||||||
|
boolean[] trackEnabledStates = trackState.trackEnabledStates;
|
||||||
int oldEnabledTrackCount = enabledTrackCount;
|
int oldEnabledTrackCount = enabledTrackCount;
|
||||||
// Deselect old tracks.
|
// Deselect old tracks.
|
||||||
for (int i = 0; i < selections.length; i++) {
|
for (int i = 0; i < selections.length; i++) {
|
||||||
@ -316,10 +320,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void discardBuffer(long positionUs, boolean toKeyframe) {
|
public void discardBuffer(long positionUs, boolean toKeyframe) {
|
||||||
|
assertPrepared();
|
||||||
if (isPendingReset()) {
|
if (isPendingReset()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean[] trackEnabledStates = Assertions.checkNotNull(preparedState).trackEnabledStates;
|
boolean[] trackEnabledStates = trackState.trackEnabledStates;
|
||||||
int trackCount = sampleQueues.length;
|
int trackCount = sampleQueues.length;
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
sampleQueues[i].discardTo(positionUs, toKeyframe, trackEnabledStates[i]);
|
sampleQueues[i].discardTo(positionUs, toKeyframe, trackEnabledStates[i]);
|
||||||
@ -336,7 +341,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
if (loadingFinished
|
if (loadingFinished
|
||||||
|| loader.hasFatalError()
|
|| loader.hasFatalError()
|
||||||
|| pendingDeferredRetry
|
|| pendingDeferredRetry
|
||||||
|| (preparedState != null && enabledTrackCount == 0)) {
|
|| (prepared && enabledTrackCount == 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean continuedLoading = loadCondition.open();
|
boolean continuedLoading = loadCondition.open();
|
||||||
@ -373,8 +378,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getBufferedPositionUs() {
|
public long getBufferedPositionUs() {
|
||||||
boolean[] trackIsAudioVideoFlags =
|
assertPrepared();
|
||||||
Assertions.checkNotNull(preparedState).trackIsAudioVideoFlags;
|
boolean[] trackIsAudioVideoFlags = trackState.trackIsAudioVideoFlags;
|
||||||
if (loadingFinished) {
|
if (loadingFinished) {
|
||||||
return C.TIME_END_OF_SOURCE;
|
return C.TIME_END_OF_SOURCE;
|
||||||
} else if (isPendingReset()) {
|
} else if (isPendingReset()) {
|
||||||
@ -400,8 +405,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long seekToUs(long positionUs) {
|
public long seekToUs(long positionUs) {
|
||||||
SeekMap seekMap = Assertions.checkNotNull(preparedState).seekMap;
|
assertPrepared();
|
||||||
boolean[] trackIsAudioVideoFlags = preparedState.trackIsAudioVideoFlags;
|
boolean[] trackIsAudioVideoFlags = trackState.trackIsAudioVideoFlags;
|
||||||
// Treat all seeks into non-seekable media as being to t=0.
|
// Treat all seeks into non-seekable media as being to t=0.
|
||||||
positionUs = seekMap.isSeekable() ? positionUs : 0;
|
positionUs = seekMap.isSeekable() ? positionUs : 0;
|
||||||
|
|
||||||
@ -436,7 +441,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
|
public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
|
||||||
SeekMap seekMap = Assertions.checkNotNull(preparedState).seekMap;
|
assertPrepared();
|
||||||
if (!seekMap.isSeekable()) {
|
if (!seekMap.isSeekable()) {
|
||||||
// Treat all seeks into non-seekable media as being to t=0.
|
// Treat all seeks into non-seekable media as being to t=0.
|
||||||
return 0;
|
return 0;
|
||||||
@ -498,10 +503,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void maybeNotifyDownstreamFormat(int track) {
|
private void maybeNotifyDownstreamFormat(int track) {
|
||||||
boolean[] trackNotifiedDownstreamFormats =
|
assertPrepared();
|
||||||
Assertions.checkNotNull(preparedState).trackNotifiedDownstreamFormats;
|
boolean[] trackNotifiedDownstreamFormats = trackState.trackNotifiedDownstreamFormats;
|
||||||
if (!trackNotifiedDownstreamFormats[track]) {
|
if (!trackNotifiedDownstreamFormats[track]) {
|
||||||
Format trackFormat = preparedState.tracks.get(track).getFormat(/* index= */ 0);
|
Format trackFormat = trackState.tracks.get(track).getFormat(/* index= */ 0);
|
||||||
eventDispatcher.downstreamFormatChanged(
|
eventDispatcher.downstreamFormatChanged(
|
||||||
MimeTypes.getTrackType(trackFormat.sampleMimeType),
|
MimeTypes.getTrackType(trackFormat.sampleMimeType),
|
||||||
trackFormat,
|
trackFormat,
|
||||||
@ -513,8 +518,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void maybeStartDeferredRetry(int track) {
|
private void maybeStartDeferredRetry(int track) {
|
||||||
boolean[] trackIsAudioVideoFlags =
|
assertPrepared();
|
||||||
Assertions.checkNotNull(preparedState).trackIsAudioVideoFlags;
|
boolean[] trackIsAudioVideoFlags = trackState.trackIsAudioVideoFlags;
|
||||||
if (!pendingDeferredRetry
|
if (!pendingDeferredRetry
|
||||||
|| !trackIsAudioVideoFlags[track]
|
|| !trackIsAudioVideoFlags[track]
|
||||||
|| sampleQueues[track].isReady(/* loadingFinished= */ false)) {
|
|| sampleQueues[track].isReady(/* loadingFinished= */ false)) {
|
||||||
@ -688,12 +693,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setSeekMap(SeekMap seekMap) {
|
private void setSeekMap(SeekMap seekMap) {
|
||||||
this.seekMap = icyHeaders == null ? seekMap : new Unseekable(/* durationUs */ C.TIME_UNSET);
|
this.seekMap = icyHeaders == null ? seekMap : new Unseekable(/* durationUs= */ C.TIME_UNSET);
|
||||||
if (preparedState == null) {
|
if (!prepared) {
|
||||||
maybeFinishPrepare();
|
maybeFinishPrepare();
|
||||||
} else {
|
|
||||||
preparedState =
|
|
||||||
new PreparedState(seekMap, preparedState.tracks, preparedState.trackIsAudioVideoFlags);
|
|
||||||
}
|
}
|
||||||
durationUs = seekMap.getDurationUs();
|
durationUs = seekMap.getDurationUs();
|
||||||
isLive = length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET;
|
isLive = length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET;
|
||||||
@ -702,8 +704,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void maybeFinishPrepare() {
|
private void maybeFinishPrepare() {
|
||||||
SeekMap seekMap = this.seekMap;
|
if (released || prepared || !sampleQueuesBuilt || seekMap == null) {
|
||||||
if (released || preparedState != null || !sampleQueuesBuilt || seekMap == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
@ -744,8 +745,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
trackArray[i] = new TrackGroup(trackFormat);
|
trackArray[i] = new TrackGroup(trackFormat);
|
||||||
}
|
}
|
||||||
preparedState =
|
trackState = new TrackState(new TrackGroupArray(trackArray), trackIsAudioVideoFlags);
|
||||||
new PreparedState(seekMap, new TrackGroupArray(trackArray), trackIsAudioVideoFlags);
|
prepared = true;
|
||||||
Assertions.checkNotNull(callback).onPrepared(this);
|
Assertions.checkNotNull(callback).onPrepared(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,8 +760,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
ExtractingLoadable loadable =
|
ExtractingLoadable loadable =
|
||||||
new ExtractingLoadable(
|
new ExtractingLoadable(
|
||||||
uri, dataSource, progressiveMediaExtractor, /* extractorOutput= */ this, loadCondition);
|
uri, dataSource, progressiveMediaExtractor, /* extractorOutput= */ this, loadCondition);
|
||||||
if (preparedState != null) {
|
if (prepared) {
|
||||||
SeekMap seekMap = preparedState.seekMap;
|
|
||||||
Assertions.checkState(isPendingReset());
|
Assertions.checkState(isPendingReset());
|
||||||
if (durationUs != C.TIME_UNSET && pendingResetPositionUs > durationUs) {
|
if (durationUs != C.TIME_UNSET && pendingResetPositionUs > durationUs) {
|
||||||
loadingFinished = true;
|
loadingFinished = true;
|
||||||
@ -768,7 +768,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loadable.setLoadPosition(
|
loadable.setLoadPosition(
|
||||||
seekMap.getSeekPoints(pendingResetPositionUs).first.position, pendingResetPositionUs);
|
Assertions.checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position,
|
||||||
|
pendingResetPositionUs);
|
||||||
pendingResetPositionUs = C.TIME_UNSET;
|
pendingResetPositionUs = C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
extractedSamplesCountAtStartOfLoad = getExtractedSamplesCount();
|
extractedSamplesCountAtStartOfLoad = getExtractedSamplesCount();
|
||||||
@ -803,7 +804,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
// request data starting from the point it left off.
|
// request data starting from the point it left off.
|
||||||
extractedSamplesCountAtStartOfLoad = currentExtractedSampleCount;
|
extractedSamplesCountAtStartOfLoad = currentExtractedSampleCount;
|
||||||
return true;
|
return true;
|
||||||
} else if (preparedState != null && !suppressRead()) {
|
} else if (prepared && !suppressRead()) {
|
||||||
// We're playing a stream of unknown length and duration. Assume it's live, and therefore that
|
// We're playing a stream of unknown length and duration. Assume it's live, and therefore that
|
||||||
// the data at the uri is a continuously shifting window of the latest available media. For
|
// the data at the uri is a continuously shifting window of the latest available media. For
|
||||||
// this case there's no way to continue loading from where a previous load finished, so it's
|
// this case there's no way to continue loading from where a previous load finished, so it's
|
||||||
@ -820,7 +821,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
// because there's no buffered data to be read. This case also covers an on-demand stream with
|
// because there's no buffered data to be read. This case also covers an on-demand stream with
|
||||||
// unknown length that has yet to be prepared. This case cannot be disambiguated from the live
|
// unknown length that has yet to be prepared. This case cannot be disambiguated from the live
|
||||||
// stream case, so we have no option but to load from the start.
|
// stream case, so we have no option but to load from the start.
|
||||||
notifyDiscontinuity = preparedState != null;
|
notifyDiscontinuity = prepared;
|
||||||
lastSeekPositionUs = 0;
|
lastSeekPositionUs = 0;
|
||||||
extractedSamplesCountAtStartOfLoad = 0;
|
extractedSamplesCountAtStartOfLoad = 0;
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
@ -875,6 +876,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return pendingResetPositionUs != C.TIME_UNSET;
|
return pendingResetPositionUs != C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EnsuresNonNull({"trackState", "seekMap"})
|
||||||
|
private void assertPrepared() {
|
||||||
|
Assertions.checkState(prepared);
|
||||||
|
Assertions.checkNotNull(trackState);
|
||||||
|
Assertions.checkNotNull(seekMap);
|
||||||
|
}
|
||||||
|
|
||||||
private final class SampleStreamImpl implements SampleStream {
|
private final class SampleStreamImpl implements SampleStream {
|
||||||
|
|
||||||
private final int track;
|
private final int track;
|
||||||
@ -1042,18 +1050,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stores state that is initialized when preparation completes. */
|
/** Stores track state. */
|
||||||
private static final class PreparedState {
|
private static final class TrackState {
|
||||||
|
|
||||||
public final SeekMap seekMap;
|
|
||||||
public final TrackGroupArray tracks;
|
public final TrackGroupArray tracks;
|
||||||
public final boolean[] trackIsAudioVideoFlags;
|
public final boolean[] trackIsAudioVideoFlags;
|
||||||
public final boolean[] trackEnabledStates;
|
public final boolean[] trackEnabledStates;
|
||||||
public final boolean[] trackNotifiedDownstreamFormats;
|
public final boolean[] trackNotifiedDownstreamFormats;
|
||||||
|
|
||||||
public PreparedState(
|
public TrackState(TrackGroupArray tracks, boolean[] trackIsAudioVideoFlags) {
|
||||||
SeekMap seekMap, TrackGroupArray tracks, boolean[] trackIsAudioVideoFlags) {
|
|
||||||
this.seekMap = seekMap;
|
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
this.trackIsAudioVideoFlags = trackIsAudioVideoFlags;
|
this.trackIsAudioVideoFlags = trackIsAudioVideoFlags;
|
||||||
this.trackEnabledStates = new boolean[tracks.length];
|
this.trackEnabledStates = new boolean[tracks.length];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user