Skip to content

Commit

Permalink
Only consider enabled tracks in ProgressiveMediaPeriod.bufferedPosition
Browse files Browse the repository at this point in the history
ProgressiveMediaPeriod loads all available tracks into SampleStreams
(because it needs to read the data anyway and it allows easy activation
of tracks without reloading). However, the SampleStreams for disabled
tracks are not read and no one if waiting for them.

The buffered position is used for user-visible state (e.g. in the UI)
and to check how much data is already buffered to decide when to stop
buffering (using LoadControl). Both values benefit from only
using the actually enabled tracks to better reflect what is available
for playback at the moment.

Issue:Issue: google/ExoPlayer#10361
PiperOrigin-RevId: 458475038
(cherry picked from commit ceb23e6)
  • Loading branch information
tonihei authored and rohitjoins committed Jul 1, 2022
1 parent 3de3f41 commit 1def7b5
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 22 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Expand Up @@ -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)).
Expand Down
Expand Up @@ -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;

Expand Down Expand Up @@ -193,8 +194,7 @@ public ProgressiveMediaPeriod(
onContinueLoadingRequestedRunnable =
() -> {
if (!released) {
Assertions.checkNotNull(callback)
.onContinueLoadingRequested(ProgressiveMediaPeriod.this);
checkNotNull(callback).onContinueLoadingRequested(ProgressiveMediaPeriod.this);
}
};
handler = Util.createHandlerForCurrentLooper();
Expand Down Expand Up @@ -366,7 +366,7 @@ public boolean isLoading() {

@Override
public long getNextLoadPositionUs() {
return enabledTrackCount == 0 ? C.TIME_END_OF_SOURCE : getBufferedPositionUs();
return getBufferedPositionUs();
}

@Override
Expand All @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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() {
Expand All @@ -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
Expand Down Expand Up @@ -578,7 +580,7 @@ public void onLoadCompleted(
/* mediaStartTimeUs= */ loadable.seekTimeUs,
durationUs);
loadingFinished = true;
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
checkNotNull(callback).onContinueLoadingRequested(this);
}

@Override
Expand Down Expand Up @@ -609,7 +611,7 @@ public void onLoadCanceled(
sampleQueue.reset();
}
if (enabledTrackCount > 0) {
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
checkNotNull(callback).onContinueLoadingRequested(this);
}
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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() {
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 1def7b5

Please sign in to comment.