Skip to content

Commit

Permalink
Split SampleQueue.advanceTo for advance & seek use cases
Browse files Browse the repository at this point in the history
This method has two use cases:

1. Seeking. Calls are immediately preceded by a call to rewind(), and
   the returned value isn't important unless it's ADVANCED_FAILED (i.e.
   the caller is only interested in success and failure).
2. Advancing. The return value is important unless it's ADVANCED_FAILED,
   in which case the caller wants to treat it as 0.

This change creates separate methods for each use case. The new seekTo
methods automatically rewind and return a boolean. The updated advanceTo
method returns 0 directly in cases where ADVANCED_FAILED was returned.
Arguments that were always hard-coded to true by callers have also been
removed.

This change is a step toward one possible solution for #6155. How we'll
solve that issue is still up for discussion, but this change seems like
one we should make regardless!

Issue: #6155
PiperOrigin-RevId: 290053743
  • Loading branch information
ojw28 committed Jan 16, 2020
1 parent 51f2723 commit 775a17c
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,13 @@ public long selectTracks(
// If there's still a chance of avoiding a seek, try and seek within the sample queue.
if (!seekRequired) {
SampleQueue sampleQueue = sampleQueues[track];
sampleQueue.rewind();
// A seek can be avoided if we're able to advance to the current playback position in the
// A seek can be avoided if we're able to seek to the current playback position in the
// sample queue, or if we haven't read anything from the queue since the previous seek
// (this case is common for sparse tracks such as metadata tracks). In all other cases a
// seek is required.
seekRequired = sampleQueue.advanceTo(positionUs, true, true) == SampleQueue.ADVANCE_FAILED
&& sampleQueue.getReadIndex() != 0;
seekRequired =
!sampleQueue.seekTo(positionUs, /* allowTimeBeyondBuffer= */ true)
&& sampleQueue.getReadIndex() != 0;
}
}
}
Expand Down Expand Up @@ -493,10 +493,7 @@ public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParame
if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) {
skipCount = sampleQueue.advanceToEnd();
} else {
skipCount = sampleQueue.advanceTo(positionUs, true, true);
if (skipCount == SampleQueue.ADVANCE_FAILED) {
skipCount = 0;
}
skipCount = sampleQueue.advanceTo(positionUs);
}
if (skipCount == 0) {
maybeStartDeferredRetry(track);
Expand Down Expand Up @@ -840,9 +837,7 @@ private boolean seekInsideBufferUs(boolean[] trackIsAudioVideoFlags, long positi
int trackCount = sampleQueues.length;
for (int i = 0; i < trackCount; i++) {
SampleQueue sampleQueue = sampleQueues[i];
sampleQueue.rewind();
boolean seekInsideQueue = sampleQueue.advanceTo(positionUs, true, false)
!= SampleQueue.ADVANCE_FAILED;
boolean seekInsideQueue = sampleQueue.seekTo(positionUs, /* allowTimeBeyondBuffer= */ false);
// If we have AV tracks then an in-buffer seek is successful if the seek into every AV queue
// is successful. We ignore whether seeks within non-AV queues are successful in this case, as
// they may be sparse or poorly interleaved. If we only have non-AV tracks then a seek is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ public interface UpstreamFormatChangedListener {
void onUpstreamFormatChanged(Format format);
}

public static final int ADVANCE_FAILED = -1;

@VisibleForTesting /* package */ static final int SAMPLE_CAPACITY_INCREMENT = 1000;

private final SampleDataQueue sampleDataQueue;
Expand Down Expand Up @@ -271,6 +269,7 @@ public synchronized long getFirstTimestampUs() {
* the end of stream has been reached. When false, this method returns false if the sample
* queue is empty.
*/
@SuppressWarnings("ReferenceEquality") // See comments in setUpstreamFormat
public synchronized boolean isReady(boolean loadingFinished) {
if (!hasNextSample()) {
return loadingFinished
Expand Down Expand Up @@ -312,7 +311,6 @@ public synchronized boolean isReady(boolean loadingFinished) {
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
* {@link C#RESULT_BUFFER_READ}.
*/
@SuppressWarnings("ReferenceEquality")
public int read(
FormatHolder formatHolder,
DecoderInputBuffer buffer,
Expand All @@ -328,36 +326,61 @@ public int read(
return result;
}

/** Rewinds the read position to the first sample in the queue. */
public void rewind() {
rewindMetadata();
sampleDataQueue.rewind();
/**
* Attempts to seek the read position to the specified sample index.
*
* @param sampleIndex The sample index.
* @return Whether the seek was successful.
*/
public synchronized boolean seekTo(int sampleIndex) {
rewind();
if (sampleIndex < absoluteFirstIndex || sampleIndex > absoluteFirstIndex + length) {
return false;
}
readPosition = sampleIndex - absoluteFirstIndex;
return true;
}

/**
* Attempts to advance the read position to the sample before or at the specified time.
* Attempts to seek the read position to the keyframe before or at the specified time.
*
* @param timeUs The time to advance to.
* @param toKeyframe If true then attempts to advance to the keyframe before or at the specified
* time, rather than to any sample before or at that time.
* @param timeUs The time to seek to.
* @param allowTimeBeyondBuffer Whether the operation can succeed if {@code timeUs} is beyond the
* end of the queue, by advancing the read position to the last sample (or keyframe).
* @return The number of samples that were skipped if the operation was successful, which may be
* equal to 0, or {@link #ADVANCE_FAILED} if the operation was not successful. A successful
* advance is one in which the read position was unchanged or advanced, and is now at a sample
* meeting the specified criteria.
* end of the queue, by seeking to the last sample (or keyframe).
* @return Whether the seek was successful.
*/
public synchronized int advanceTo(
long timeUs, boolean toKeyframe, boolean allowTimeBeyondBuffer) {
public synchronized boolean seekTo(long timeUs, boolean allowTimeBeyondBuffer) {
rewind();
int relativeReadIndex = getRelativeIndex(readPosition);
if (!hasNextSample()
|| timeUs < timesUs[relativeReadIndex]
|| (timeUs > largestQueuedTimestampUs && !allowTimeBeyondBuffer)) {
return SampleQueue.ADVANCE_FAILED;
return false;
}
int offset = findSampleBefore(relativeReadIndex, length - readPosition, timeUs, toKeyframe);
int offset =
findSampleBefore(relativeReadIndex, length - readPosition, timeUs, /* keyframe= */ true);
if (offset == -1) {
return SampleQueue.ADVANCE_FAILED;
return false;
}
readPosition += offset;
return true;
}

/**
* Advances the read position to the keyframe before or at the specified time.
*
* @param timeUs The time to advance to.
* @return The number of samples that were skipped, which may be equal to 0.
*/
public synchronized int advanceTo(long timeUs) {
int relativeReadIndex = getRelativeIndex(readPosition);
if (!hasNextSample() || timeUs < timesUs[relativeReadIndex]) {
return 0;
}
int offset =
findSampleBefore(relativeReadIndex, length - readPosition, timeUs, /* keyframe= */ true);
if (offset == -1) {
return 0;
}
readPosition += offset;
return offset;
Expand All @@ -374,22 +397,6 @@ public synchronized int advanceToEnd() {
return skipCount;
}

/**
* Attempts to set the read position to the specified sample index.
*
* @param sampleIndex The sample index.
* @return Whether the read position was set successfully. False is returned if the specified
* index is smaller than the index of the first sample in the queue, or larger than the index
* of the next sample that will be written.
*/
public synchronized boolean setReadPosition(int sampleIndex) {
if (absoluteFirstIndex <= sampleIndex && sampleIndex <= absoluteFirstIndex + length) {
readPosition = sampleIndex - absoluteFirstIndex;
return true;
}
return false;
}

/**
* Discards up to but not including the sample immediately before or at the specified time.
*
Expand Down Expand Up @@ -486,10 +493,13 @@ public void sampleMetadata(

// Internal methods.

private synchronized void rewindMetadata() {
/** Rewinds the read position to the first sample in the queue. */
private synchronized void rewind() {
readPosition = 0;
sampleDataQueue.rewind();
}

@SuppressWarnings("ReferenceEquality") // See comments in setUpstreamFormat
private synchronized int readSampleMetadata(
FormatHolder formatHolder,
DecoderInputBuffer buffer,
Expand Down Expand Up @@ -543,13 +553,13 @@ private synchronized boolean setUpstreamFormat(Format format) {
upstreamFormatRequired = false;
if (Util.areEqual(format, upstreamFormat)) {
// The format is unchanged. If format and upstreamFormat are different objects, we keep the
// current upstreamFormat so we can detect format changes in read() using cheap referential
// equality.
// current upstreamFormat so we can detect format changes on the read side using cheap
// referential quality.
return false;
} else if (Util.areEqual(format, upstreamCommittedFormat)) {
// The format has changed back to the format of the last committed sample. If they are
// different objects, we revert back to using upstreamCommittedFormat as the upstreamFormat
// so we can detect format changes in read() using cheap referential equality.
// so we can detect format changes on the read side using cheap referential equality.
upstreamFormat = upstreamCommittedFormat;
return true;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,7 @@ public EmbeddedSampleStream selectEmbeddedTrack(long positionUs, int trackType)
if (embeddedTrackTypes[i] == trackType) {
Assertions.checkState(!embeddedTracksSelected[i]);
embeddedTracksSelected[i] = true;
embeddedSampleQueues[i].rewind();
embeddedSampleQueues[i].advanceTo(positionUs, true, true);
embeddedSampleQueues[i].seekTo(positionUs, /* allowTimeBeyondBuffer= */ true);
return new EmbeddedSampleStream(this, embeddedSampleQueues[i], i);
}
}
Expand Down Expand Up @@ -267,21 +266,16 @@ public void seekToUs(long positionUs) {

// See if we can seek inside the primary sample queue.
boolean seekInsideBuffer;
primarySampleQueue.rewind();
if (seekToMediaChunk != null) {
// When seeking to the start of a chunk we use the index of the first sample in the chunk
// rather than the seek position. This ensures we seek to the keyframe at the start of the
// chunk even if the sample timestamps are slightly offset from the chunk start times.
seekInsideBuffer =
primarySampleQueue.setReadPosition(seekToMediaChunk.getFirstSampleIndex(0));
seekInsideBuffer = primarySampleQueue.seekTo(seekToMediaChunk.getFirstSampleIndex(0));
decodeOnlyUntilPositionUs = 0;
} else {
seekInsideBuffer =
primarySampleQueue.advanceTo(
positionUs,
/* toKeyframe= */ true,
/* allowTimeBeyondBuffer= */ positionUs < getNextLoadPositionUs())
!= SampleQueue.ADVANCE_FAILED;
primarySampleQueue.seekTo(
positionUs, /* allowTimeBeyondBuffer= */ positionUs < getNextLoadPositionUs());
decodeOnlyUntilPositionUs = lastSeekPositionUs;
}

Expand All @@ -290,10 +284,9 @@ public void seekToUs(long positionUs) {
nextNotifyPrimaryFormatMediaChunkIndex =
primarySampleIndexToMediaChunkIndex(
primarySampleQueue.getReadIndex(), /* minChunkIndex= */ 0);
// Advance the embedded sample queues to the seek position.
// Seek the embedded sample queues.
for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) {
embeddedSampleQueue.rewind();
embeddedSampleQueue.advanceTo(positionUs, true, false);
embeddedSampleQueue.seekTo(positionUs, /* allowTimeBeyondBuffer= */ true);
}
} else {
// We can't seek inside the buffer, and so need to reset.
Expand Down Expand Up @@ -390,10 +383,7 @@ public int skipData(long positionUs) {
if (loadingFinished && positionUs > primarySampleQueue.getLargestQueuedTimestampUs()) {
skipCount = primarySampleQueue.advanceToEnd();
} else {
skipCount = primarySampleQueue.advanceTo(positionUs, true, true);
if (skipCount == SampleQueue.ADVANCE_FAILED) {
skipCount = 0;
}
skipCount = primarySampleQueue.advanceTo(positionUs);
}
maybeNotifyPrimaryTrackFormatChanged();
return skipCount;
Expand Down Expand Up @@ -756,10 +746,7 @@ public int skipData(long positionUs) {
if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) {
skipCount = sampleQueue.advanceToEnd();
} else {
skipCount = sampleQueue.advanceTo(positionUs, true, true);
if (skipCount == SampleQueue.ADVANCE_FAILED) {
skipCount = 0;
}
skipCount = sampleQueue.advanceTo(positionUs);
}
return skipCount;
}
Expand Down
Loading

0 comments on commit 775a17c

Please sign in to comment.