diff --git a/RELEASENOTES.md b/RELEASENOTES.md index bdd71a9fa0..b2cd791e8d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -7,6 +7,8 @@ results in a call to `Player.Listener#onTimelineChanged` with `reason=Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED` ([#9889](https://github.com/google/ExoPlayer/issues/9889)). + * For progressive media, only include selected tracks in buffered position + ([#10361](https://github.com/google/ExoPlayer/issues/10361)). * Extractors: * Add support for AVI ([#2092](https://github.com/google/ExoPlayer/issues/2092)). diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java index 1f83a065fc..7471f3a2bd 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer.source; +import static androidx.media3.common.util.Assertions.checkNotNull; import static java.lang.Math.max; import static java.lang.Math.min; @@ -193,8 +194,7 @@ public ProgressiveMediaPeriod( onContinueLoadingRequestedRunnable = () -> { if (!released) { - Assertions.checkNotNull(callback) - .onContinueLoadingRequested(ProgressiveMediaPeriod.this); + checkNotNull(callback).onContinueLoadingRequested(ProgressiveMediaPeriod.this); } }; handler = Util.createHandlerForCurrentLooper(); @@ -366,7 +366,7 @@ public boolean isLoading() { @Override public long getNextLoadPositionUs() { - return enabledTrackCount == 0 ? C.TIME_END_OF_SOURCE : getBufferedPositionUs(); + return getBufferedPositionUs(); } @Override @@ -382,8 +382,7 @@ public long readDiscontinuity() { @Override public long getBufferedPositionUs() { assertPrepared(); - boolean[] trackIsAudioVideoFlags = trackState.trackIsAudioVideoFlags; - if (loadingFinished) { + if (loadingFinished || enabledTrackCount == 0) { return C.TIME_END_OF_SOURCE; } else if (isPendingReset()) { return pendingResetPositionUs; @@ -393,14 +392,16 @@ public long getBufferedPositionUs() { // Ignore non-AV tracks, which may be sparse or poorly interleaved. int trackCount = sampleQueues.length; for (int i = 0; i < trackCount; i++) { - if (trackIsAudioVideoFlags[i] && !sampleQueues[i].isLastSampleQueued()) { + if (trackState.trackIsAudioVideoFlags[i] + && trackState.trackEnabledStates[i] + && !sampleQueues[i].isLastSampleQueued()) { largestQueuedTimestampUs = min(largestQueuedTimestampUs, sampleQueues[i].getLargestQueuedTimestampUs()); } } } if (largestQueuedTimestampUs == Long.MAX_VALUE) { - largestQueuedTimestampUs = getLargestQueuedTimestampUs(); + largestQueuedTimestampUs = getLargestQueuedTimestampUs(/* includeDisabledTracks= */ false); } return largestQueuedTimestampUs == Long.MIN_VALUE ? lastSeekPositionUs @@ -536,7 +537,7 @@ private void maybeStartDeferredRetry(int track) { for (SampleQueue sampleQueue : sampleQueues) { sampleQueue.reset(); } - Assertions.checkNotNull(callback).onContinueLoadingRequested(this); + checkNotNull(callback).onContinueLoadingRequested(this); } private boolean suppressRead() { @@ -550,7 +551,8 @@ public void onLoadCompleted( ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { if (durationUs == C.TIME_UNSET && seekMap != null) { boolean isSeekable = seekMap.isSeekable(); - long largestQueuedTimestampUs = getLargestQueuedTimestampUs(); + long largestQueuedTimestampUs = + getLargestQueuedTimestampUs(/* includeDisabledTracks= */ true); durationUs = largestQueuedTimestampUs == Long.MIN_VALUE ? 0 @@ -578,7 +580,7 @@ public void onLoadCompleted( /* mediaStartTimeUs= */ loadable.seekTimeUs, durationUs); loadingFinished = true; - Assertions.checkNotNull(callback).onContinueLoadingRequested(this); + checkNotNull(callback).onContinueLoadingRequested(this); } @Override @@ -609,7 +611,7 @@ public void onLoadCanceled( sampleQueue.reset(); } if (enabledTrackCount > 0) { - Assertions.checkNotNull(callback).onContinueLoadingRequested(this); + checkNotNull(callback).onContinueLoadingRequested(this); } } } @@ -755,7 +757,7 @@ private void maybeFinishPrepare() { TrackGroup[] trackArray = new TrackGroup[trackCount]; boolean[] trackIsAudioVideoFlags = new boolean[trackCount]; for (int i = 0; i < trackCount; i++) { - Format trackFormat = Assertions.checkNotNull(sampleQueues[i].getUpstreamFormat()); + Format trackFormat = checkNotNull(sampleQueues[i].getUpstreamFormat()); @Nullable String mimeType = trackFormat.sampleMimeType; boolean isAudio = MimeTypes.isAudio(mimeType); boolean isAudioVideo = isAudio || MimeTypes.isVideo(mimeType); @@ -786,7 +788,7 @@ private void maybeFinishPrepare() { } trackState = new TrackState(new TrackGroupArray(trackArray), trackIsAudioVideoFlags); prepared = true; - Assertions.checkNotNull(callback).onPrepared(this); + checkNotNull(callback).onPrepared(this); } private void startLoading() { @@ -801,7 +803,7 @@ private void startLoading() { return; } loadable.setLoadPosition( - Assertions.checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position, + checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position, pendingResetPositionUs); for (SampleQueue sampleQueue : sampleQueues) { sampleQueue.setStartTimeUs(pendingResetPositionUs); @@ -898,11 +900,13 @@ private int getExtractedSamplesCount() { return extractedSamplesCount; } - private long getLargestQueuedTimestampUs() { + private long getLargestQueuedTimestampUs(boolean includeDisabledTracks) { long largestQueuedTimestampUs = Long.MIN_VALUE; - for (SampleQueue sampleQueue : sampleQueues) { - largestQueuedTimestampUs = - max(largestQueuedTimestampUs, sampleQueue.getLargestQueuedTimestampUs()); + for (int i = 0; i < sampleQueues.length; i++) { + if (includeDisabledTracks || checkNotNull(trackState).trackEnabledStates[i]) { + largestQueuedTimestampUs = + max(largestQueuedTimestampUs, sampleQueues[i].getLargestQueuedTimestampUs()); + } } return largestQueuedTimestampUs; } @@ -914,8 +918,8 @@ private boolean isPendingReset() { @EnsuresNonNull({"trackState", "seekMap"}) private void assertPrepared() { Assertions.checkState(prepared); - Assertions.checkNotNull(trackState); - Assertions.checkNotNull(seekMap); + checkNotNull(trackState); + checkNotNull(seekMap); } private final class SampleStreamImpl implements SampleStream { @@ -1058,9 +1062,12 @@ public void load() throws IOException { public void onIcyMetadata(ParsableByteArray metadata) { // Always output the first ICY metadata at the start time. This helps minimize any delay // between the start of playback and the first ICY metadata event. - long timeUs = !seenIcyMetadata ? seekTimeUs : max(getLargestQueuedTimestampUs(), seekTimeUs); + long timeUs = + !seenIcyMetadata + ? seekTimeUs + : max(getLargestQueuedTimestampUs(/* includeDisabledTracks= */ true), seekTimeUs); int length = metadata.bytesLeft(); - TrackOutput icyTrackOutput = Assertions.checkNotNull(this.icyTrackOutput); + TrackOutput icyTrackOutput = checkNotNull(this.icyTrackOutput); icyTrackOutput.sampleData(metadata, length); icyTrackOutput.sampleMetadata( timeUs, C.BUFFER_FLAG_KEY_FRAME, length, /* offset= */ 0, /* cryptoData= */ null);