From b31438b67065e6d95fe86298b1244057043aaacc Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 11 Sep 2018 06:43:32 -0700 Subject: [PATCH] Clean up downstream format notification for ChunkSampleStream - Fixes the referenced issue. - Provides more consistent behavior for the edge case of empty chunks (i.e. chunks containing now samples). Format changes will now always be reported for such chunks, unless the empty chunk is discarded from upstream before there's a chance to do so. Issue: #4533 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=212443043 --- .../source/chunk/ChunkSampleStream.java | 72 +++++++++++-------- 1 file changed, 43 insertions(+), 29 deletions(-) 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));