diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java index 21c0fdfc7a..1f245784e7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java @@ -79,6 +79,8 @@ public class ChunkSampleStream implements SampleStream, S private @Nullable ReleaseCallback releaseCallback; private long pendingResetPositionUs; private long lastSeekPositionUs; + private int nextNotifyPrimaryFormatMediaChunkIndex; + /* package */ long decodeOnlyUntilPositionUs; /* package */ boolean loadingFinished; @@ -191,16 +193,16 @@ public class ChunkSampleStream implements SampleStream, S if (isPendingReset()) { return; } - int oldFirstIndex = primarySampleQueue.getFirstIndex(); + int oldFirstSampleIndex = primarySampleQueue.getFirstIndex(); primarySampleQueue.discardTo(positionUs, toKeyframe, true); - int newFirstIndex = primarySampleQueue.getFirstIndex(); - if (newFirstIndex > oldFirstIndex) { + int newFirstSampleIndex = primarySampleQueue.getFirstIndex(); + if (newFirstSampleIndex > oldFirstSampleIndex) { long discardToUs = primarySampleQueue.getFirstTimestampUs(); for (int i = 0; i < embeddedSampleQueues.length; i++) { embeddedSampleQueues[i].discardTo(discardToUs, toKeyframe, embeddedTracksSelected[i]); } - discardDownstreamMediaChunks(newFirstIndex); } + discardDownstreamMediaChunks(newFirstSampleIndex); } /** @@ -318,7 +320,11 @@ public class ChunkSampleStream implements SampleStream, S } if (seekInsideBuffer) { - // We can seek inside the buffer. Advance the embedded sample queues to the seek position. + // We can seek inside the buffer. + nextNotifyPrimaryFormatMediaChunkIndex = + primarySampleIndexToMediaChunkIndex( + primarySampleQueue.getReadIndex(), /* minChunkIndex= */ 0); + // Advance the embedded sample queues to the seek position. for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) { embeddedSampleQueue.rewind(); embeddedSampleQueue.advanceTo(positionUs, true, false); @@ -328,6 +334,7 @@ public class ChunkSampleStream implements SampleStream, S pendingResetPositionUs = positionUs; loadingFinished = false; mediaChunks.clear(); + nextNotifyPrimaryFormatMediaChunkIndex = 0; if (loader.isLoading()) { loader.cancelLoading(); } else { @@ -400,13 +407,9 @@ public class ChunkSampleStream implements SampleStream, S if (isPendingReset()) { return C.RESULT_NOTHING_READ; } - int result = - primarySampleQueue.read( - formatHolder, buffer, formatRequired, loadingFinished, decodeOnlyUntilPositionUs); - if (result == C.RESULT_BUFFER_READ) { - maybeNotifyPrimaryTrackFormatChanged(primarySampleQueue.getReadIndex(), 1); - } - return result; + maybeNotifyPrimaryTrackFormatChanged(); + return primarySampleQueue.read( + formatHolder, buffer, formatRequired, loadingFinished, decodeOnlyUntilPositionUs); } @Override @@ -423,9 +426,7 @@ public class ChunkSampleStream implements SampleStream, S skipCount = 0; } } - if (skipCount > 0) { - maybeNotifyPrimaryTrackFormatChanged(primarySampleQueue.getReadIndex(), skipCount); - } + maybeNotifyPrimaryTrackFormatChanged(); return skipCount; } @@ -669,22 +670,25 @@ public class ChunkSampleStream implements SampleStream, S return pendingResetPositionUs != C.TIME_UNSET; } - private void discardDownstreamMediaChunks(int discardToPrimaryStreamIndex) { + private void discardDownstreamMediaChunks(int discardToSampleIndex) { int discardToMediaChunkIndex = - primaryStreamIndexToMediaChunkIndex(discardToPrimaryStreamIndex, /* minChunkIndex= */ 0); + primarySampleIndexToMediaChunkIndex(discardToSampleIndex, /* minChunkIndex= */ 0); + // Don't discard any chunks that we haven't reported the primary format change for yet. + discardToMediaChunkIndex = + Math.min(discardToMediaChunkIndex, nextNotifyPrimaryFormatMediaChunkIndex); if (discardToMediaChunkIndex > 0) { Util.removeRange(mediaChunks, /* fromIndex= */ 0, /* toIndex= */ discardToMediaChunkIndex); + nextNotifyPrimaryFormatMediaChunkIndex -= discardToMediaChunkIndex; } } - private void maybeNotifyPrimaryTrackFormatChanged(int toPrimaryStreamReadIndex, int readCount) { - int fromMediaChunkIndex = primaryStreamIndexToMediaChunkIndex( - toPrimaryStreamReadIndex - readCount, /* minChunkIndex= */ 0); - int toMediaChunkIndexInclusive = readCount == 1 ? fromMediaChunkIndex - : primaryStreamIndexToMediaChunkIndex(toPrimaryStreamReadIndex - 1, - /* minChunkIndex= */ fromMediaChunkIndex); - for (int i = fromMediaChunkIndex; i <= toMediaChunkIndexInclusive; i++) { - maybeNotifyPrimaryTrackFormatChanged(i); + private void maybeNotifyPrimaryTrackFormatChanged() { + int readSampleIndex = primarySampleQueue.getReadIndex(); + int notifyToMediaChunkIndex = + primarySampleIndexToMediaChunkIndex( + readSampleIndex, /* minChunkIndex= */ nextNotifyPrimaryFormatMediaChunkIndex - 1); + while (nextNotifyPrimaryFormatMediaChunkIndex <= notifyToMediaChunkIndex) { + maybeNotifyPrimaryTrackFormatChanged(nextNotifyPrimaryFormatMediaChunkIndex++); } } @@ -700,12 +704,20 @@ public class ChunkSampleStream implements SampleStream, S } /** - * Returns media chunk index for primary stream sample index. May be -1 if the list of media - * chunks is empty or the requested index is less than the first index in the first media chunk. + * Returns the media chunk index corresponding to a given primary sample index. + * + * @param primarySampleIndex The primary sample index for which the corresponding media chunk + * index is required. + * @param minChunkIndex A minimum chunk index from which to start searching, or -1 if no hint can + * be provided. + * @return The index of the media chunk corresponding to the sample index, or -1 if the list of + * media chunks is empty, or {@code minChunkIndex} if the sample precedes the first chunk in + * the search (i.e. the chunk at {@code minChunkIndex}, or at index 0 if {@code minChunkIndex} + * is -1. */ - private int primaryStreamIndexToMediaChunkIndex(int primaryStreamIndex, int minChunkIndex) { + private int primarySampleIndexToMediaChunkIndex(int primarySampleIndex, int minChunkIndex) { for (int i = minChunkIndex + 1; i < mediaChunks.size(); i++) { - if (mediaChunks.get(i).getFirstSampleIndex(0) > primaryStreamIndex) { + if (mediaChunks.get(i).getFirstSampleIndex(0) > primarySampleIndex) { return i - 1; } } @@ -726,6 +738,8 @@ public class ChunkSampleStream implements SampleStream, S private BaseMediaChunk discardUpstreamMediaChunksFromIndex(int chunkIndex) { BaseMediaChunk firstRemovedChunk = mediaChunks.get(chunkIndex); Util.removeRange(mediaChunks, /* fromIndex= */ chunkIndex, /* toIndex= */ mediaChunks.size()); + nextNotifyPrimaryFormatMediaChunkIndex = + Math.max(nextNotifyPrimaryFormatMediaChunkIndex, mediaChunks.size()); primarySampleQueue.discardUpstreamSamples(firstRemovedChunk.getFirstSampleIndex(0)); for (int i = 0; i < embeddedSampleQueues.length; i++) { embeddedSampleQueues[i].discardUpstreamSamples(firstRemovedChunk.getFirstSampleIndex(i + 1));