Only consider enabled tracks in ProgressiveMediaPeriod.bufferedPosition
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
This commit is contained in:
parent
3df4f3eb19
commit
ceb23e69bb
@ -7,6 +7,8 @@
|
|||||||
results in a call to `Player.Listener#onTimelineChanged` with
|
results in a call to `Player.Listener#onTimelineChanged` with
|
||||||
`reason=Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED`
|
`reason=Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED`
|
||||||
([#9889](https://github.com/google/ExoPlayer/issues/9889)).
|
([#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:
|
* Extractors:
|
||||||
* Add support for AVI
|
* Add support for AVI
|
||||||
([#2092](https://github.com/google/ExoPlayer/issues/2092)).
|
([#2092](https://github.com/google/ExoPlayer/issues/2092)).
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.source;
|
package androidx.media3.exoplayer.source;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
@ -193,8 +194,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
onContinueLoadingRequestedRunnable =
|
onContinueLoadingRequestedRunnable =
|
||||||
() -> {
|
() -> {
|
||||||
if (!released) {
|
if (!released) {
|
||||||
Assertions.checkNotNull(callback)
|
checkNotNull(callback).onContinueLoadingRequested(ProgressiveMediaPeriod.this);
|
||||||
.onContinueLoadingRequested(ProgressiveMediaPeriod.this);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
handler = Util.createHandlerForCurrentLooper();
|
handler = Util.createHandlerForCurrentLooper();
|
||||||
@ -366,7 +366,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getNextLoadPositionUs() {
|
public long getNextLoadPositionUs() {
|
||||||
return enabledTrackCount == 0 ? C.TIME_END_OF_SOURCE : getBufferedPositionUs();
|
return getBufferedPositionUs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -382,8 +382,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@Override
|
@Override
|
||||||
public long getBufferedPositionUs() {
|
public long getBufferedPositionUs() {
|
||||||
assertPrepared();
|
assertPrepared();
|
||||||
boolean[] trackIsAudioVideoFlags = trackState.trackIsAudioVideoFlags;
|
if (loadingFinished || enabledTrackCount == 0) {
|
||||||
if (loadingFinished) {
|
|
||||||
return C.TIME_END_OF_SOURCE;
|
return C.TIME_END_OF_SOURCE;
|
||||||
} else if (isPendingReset()) {
|
} else if (isPendingReset()) {
|
||||||
return pendingResetPositionUs;
|
return pendingResetPositionUs;
|
||||||
@ -393,14 +392,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
// Ignore non-AV tracks, which may be sparse or poorly interleaved.
|
// Ignore non-AV tracks, which may be sparse or poorly interleaved.
|
||||||
int trackCount = sampleQueues.length;
|
int trackCount = sampleQueues.length;
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
if (trackIsAudioVideoFlags[i] && !sampleQueues[i].isLastSampleQueued()) {
|
if (trackState.trackIsAudioVideoFlags[i]
|
||||||
|
&& trackState.trackEnabledStates[i]
|
||||||
|
&& !sampleQueues[i].isLastSampleQueued()) {
|
||||||
largestQueuedTimestampUs =
|
largestQueuedTimestampUs =
|
||||||
min(largestQueuedTimestampUs, sampleQueues[i].getLargestQueuedTimestampUs());
|
min(largestQueuedTimestampUs, sampleQueues[i].getLargestQueuedTimestampUs());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (largestQueuedTimestampUs == Long.MAX_VALUE) {
|
if (largestQueuedTimestampUs == Long.MAX_VALUE) {
|
||||||
largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
largestQueuedTimestampUs = getLargestQueuedTimestampUs(/* includeDisabledTracks= */ false);
|
||||||
}
|
}
|
||||||
return largestQueuedTimestampUs == Long.MIN_VALUE
|
return largestQueuedTimestampUs == Long.MIN_VALUE
|
||||||
? lastSeekPositionUs
|
? lastSeekPositionUs
|
||||||
@ -536,7 +537,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
sampleQueue.reset();
|
sampleQueue.reset();
|
||||||
}
|
}
|
||||||
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
checkNotNull(callback).onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean suppressRead() {
|
private boolean suppressRead() {
|
||||||
@ -550,7 +551,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) {
|
ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) {
|
||||||
if (durationUs == C.TIME_UNSET && seekMap != null) {
|
if (durationUs == C.TIME_UNSET && seekMap != null) {
|
||||||
boolean isSeekable = seekMap.isSeekable();
|
boolean isSeekable = seekMap.isSeekable();
|
||||||
long largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
long largestQueuedTimestampUs =
|
||||||
|
getLargestQueuedTimestampUs(/* includeDisabledTracks= */ true);
|
||||||
durationUs =
|
durationUs =
|
||||||
largestQueuedTimestampUs == Long.MIN_VALUE
|
largestQueuedTimestampUs == Long.MIN_VALUE
|
||||||
? 0
|
? 0
|
||||||
@ -578,7 +580,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
/* mediaStartTimeUs= */ loadable.seekTimeUs,
|
/* mediaStartTimeUs= */ loadable.seekTimeUs,
|
||||||
durationUs);
|
durationUs);
|
||||||
loadingFinished = true;
|
loadingFinished = true;
|
||||||
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
checkNotNull(callback).onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -609,7 +611,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
sampleQueue.reset();
|
sampleQueue.reset();
|
||||||
}
|
}
|
||||||
if (enabledTrackCount > 0) {
|
if (enabledTrackCount > 0) {
|
||||||
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
checkNotNull(callback).onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -755,7 +757,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
TrackGroup[] trackArray = new TrackGroup[trackCount];
|
TrackGroup[] trackArray = new TrackGroup[trackCount];
|
||||||
boolean[] trackIsAudioVideoFlags = new boolean[trackCount];
|
boolean[] trackIsAudioVideoFlags = new boolean[trackCount];
|
||||||
for (int i = 0; i < trackCount; i++) {
|
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;
|
@Nullable String mimeType = trackFormat.sampleMimeType;
|
||||||
boolean isAudio = MimeTypes.isAudio(mimeType);
|
boolean isAudio = MimeTypes.isAudio(mimeType);
|
||||||
boolean isAudioVideo = isAudio || MimeTypes.isVideo(mimeType);
|
boolean isAudioVideo = isAudio || MimeTypes.isVideo(mimeType);
|
||||||
@ -786,7 +788,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
trackState = new TrackState(new TrackGroupArray(trackArray), trackIsAudioVideoFlags);
|
trackState = new TrackState(new TrackGroupArray(trackArray), trackIsAudioVideoFlags);
|
||||||
prepared = true;
|
prepared = true;
|
||||||
Assertions.checkNotNull(callback).onPrepared(this);
|
checkNotNull(callback).onPrepared(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLoading() {
|
private void startLoading() {
|
||||||
@ -801,7 +803,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loadable.setLoadPosition(
|
loadable.setLoadPosition(
|
||||||
Assertions.checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position,
|
checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position,
|
||||||
pendingResetPositionUs);
|
pendingResetPositionUs);
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
sampleQueue.setStartTimeUs(pendingResetPositionUs);
|
sampleQueue.setStartTimeUs(pendingResetPositionUs);
|
||||||
@ -898,11 +900,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return extractedSamplesCount;
|
return extractedSamplesCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getLargestQueuedTimestampUs() {
|
private long getLargestQueuedTimestampUs(boolean includeDisabledTracks) {
|
||||||
long largestQueuedTimestampUs = Long.MIN_VALUE;
|
long largestQueuedTimestampUs = Long.MIN_VALUE;
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (int i = 0; i < sampleQueues.length; i++) {
|
||||||
largestQueuedTimestampUs =
|
if (includeDisabledTracks || checkNotNull(trackState).trackEnabledStates[i]) {
|
||||||
max(largestQueuedTimestampUs, sampleQueue.getLargestQueuedTimestampUs());
|
largestQueuedTimestampUs =
|
||||||
|
max(largestQueuedTimestampUs, sampleQueues[i].getLargestQueuedTimestampUs());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return largestQueuedTimestampUs;
|
return largestQueuedTimestampUs;
|
||||||
}
|
}
|
||||||
@ -914,8 +918,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@EnsuresNonNull({"trackState", "seekMap"})
|
@EnsuresNonNull({"trackState", "seekMap"})
|
||||||
private void assertPrepared() {
|
private void assertPrepared() {
|
||||||
Assertions.checkState(prepared);
|
Assertions.checkState(prepared);
|
||||||
Assertions.checkNotNull(trackState);
|
checkNotNull(trackState);
|
||||||
Assertions.checkNotNull(seekMap);
|
checkNotNull(seekMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class SampleStreamImpl implements SampleStream {
|
private final class SampleStreamImpl implements SampleStream {
|
||||||
@ -1058,9 +1062,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
public void onIcyMetadata(ParsableByteArray metadata) {
|
public void onIcyMetadata(ParsableByteArray metadata) {
|
||||||
// Always output the first ICY metadata at the start time. This helps minimize any delay
|
// 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.
|
// 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();
|
int length = metadata.bytesLeft();
|
||||||
TrackOutput icyTrackOutput = Assertions.checkNotNull(this.icyTrackOutput);
|
TrackOutput icyTrackOutput = checkNotNull(this.icyTrackOutput);
|
||||||
icyTrackOutput.sampleData(metadata, length);
|
icyTrackOutput.sampleData(metadata, length);
|
||||||
icyTrackOutput.sampleMetadata(
|
icyTrackOutput.sampleMetadata(
|
||||||
timeUs, C.BUFFER_FLAG_KEY_FRAME, length, /* offset= */ 0, /* cryptoData= */ null);
|
timeUs, C.BUFFER_FLAG_KEY_FRAME, length, /* offset= */ 0, /* cryptoData= */ null);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user