mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Split TrackSelection.evalauteQueueSize in discard and cancelation.
The option to cancel ongoing loads as part of the queue size evalation was added recently. This split out the decision to a new method so that a TrackSelection implementation can independently cancel loads and discard upstream data. It also clarifies that evaluateQueueSize will only be called if there is no ongoing load. Issue: #2848 PiperOrigin-RevId: 315659735
This commit is contained in:
parent
2a9144fa56
commit
95b61eb835
@ -88,6 +88,9 @@
|
||||
([#7332](https://github.com/google/ExoPlayer/issues/7332)).
|
||||
* Add `HttpDataSource.InvalidResponseCodeException#responseBody` field
|
||||
([#6853](https://github.com/google/ExoPlayer/issues/6853)).
|
||||
* Add `TrackSelection.shouldCancelMediaChunkLoad` to check whether an
|
||||
ongoing load should be canceled. Only supported by HLS streams so far.
|
||||
([#2848](https://github.com/google/ExoPlayer/issues/2848)).
|
||||
* Video: Pass frame rate hint to `Surface.setFrameRate` on Android R devices.
|
||||
* Text:
|
||||
* Parse `<ruby>` and `<rt>` tags in WebVTT subtitles (rendering is coming
|
||||
|
@ -239,8 +239,8 @@ public interface MediaPeriod extends SequenceableLoader {
|
||||
*
|
||||
* <p>This method is only called after the period has been prepared.
|
||||
*
|
||||
* <p>A period may choose to discard buffered media so that it can be re-buffered in a different
|
||||
* quality.
|
||||
* <p>A period may choose to discard buffered media or cancel ongoing loads so that media can be
|
||||
* re-buffered in a different quality.
|
||||
*
|
||||
* @param positionUs The current playback position in microseconds. If playback of this period has
|
||||
* not yet started, the value will be the starting position in this period minus the duration
|
||||
|
@ -66,8 +66,8 @@ public interface SequenceableLoader {
|
||||
/**
|
||||
* Re-evaluates the buffer given the playback position.
|
||||
*
|
||||
* <p>Re-evaluation may discard buffered media so that it can be re-buffered in a different
|
||||
* quality.
|
||||
* <p>Re-evaluation may discard buffered media or cancel ongoing loads so that media can be
|
||||
* re-buffered in a different quality.
|
||||
*
|
||||
* @param positionUs The current playback position in microseconds. If playback of this period has
|
||||
* not yet started, the value will be the starting position in this period minus the duration
|
||||
|
@ -38,8 +38,6 @@ public interface ChunkSource {
|
||||
/**
|
||||
* If the source is currently having difficulty providing chunks, then this method throws the
|
||||
* underlying error. Otherwise does nothing.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared.
|
||||
*
|
||||
* @throws IOException The underlying error.
|
||||
*/
|
||||
@ -47,10 +45,12 @@ public interface ChunkSource {
|
||||
|
||||
/**
|
||||
* Evaluates whether {@link MediaChunk}s should be removed from the back of the queue.
|
||||
* <p>
|
||||
* Removing {@link MediaChunk}s from the back of the queue can be useful if they could be replaced
|
||||
* with chunks of a significantly higher quality (e.g. because the available bandwidth has
|
||||
* substantially increased).
|
||||
*
|
||||
* <p>Removing {@link MediaChunk}s from the back of the queue can be useful if they could be
|
||||
* replaced with chunks of a significantly higher quality (e.g. because the available bandwidth
|
||||
* has substantially increased).
|
||||
*
|
||||
* <p>Will only be called if no {@link MediaChunk} in the queue is currently loading.
|
||||
*
|
||||
* @param playbackPositionUs The current playback position.
|
||||
* @param queue The queue of buffered {@link MediaChunk}s.
|
||||
@ -85,8 +85,6 @@ public interface ChunkSource {
|
||||
* Called when the {@link ChunkSampleStream} has finished loading a chunk obtained from this
|
||||
* source.
|
||||
*
|
||||
* <p>This method should only be called when the source is enabled.
|
||||
*
|
||||
* @param chunk The chunk whose load has been completed.
|
||||
*/
|
||||
void onChunkLoadCompleted(Chunk chunk);
|
||||
@ -95,8 +93,6 @@ public interface ChunkSource {
|
||||
* Called when the {@link ChunkSampleStream} encounters an error loading a chunk obtained from
|
||||
* this source.
|
||||
*
|
||||
* <p>This method should only be called when the source is enabled.
|
||||
*
|
||||
* @param chunk The chunk whose load encountered the error.
|
||||
* @param cancelable Whether the load can be canceled.
|
||||
* @param e The error.
|
||||
|
@ -19,6 +19,7 @@ import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.chunk.Chunk;
|
||||
import com.google.android.exoplayer2.source.chunk.MediaChunk;
|
||||
import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
|
||||
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
||||
@ -93,8 +94,8 @@ public interface TrackSelection {
|
||||
|
||||
/**
|
||||
* Enables the track selection. Dynamic changes via {@link #updateSelectedTrack(long, long, long,
|
||||
* List, MediaChunkIterator[])} or {@link #evaluateQueueSize(long, List)} will only happen after
|
||||
* this call.
|
||||
* List, MediaChunkIterator[])}, {@link #evaluateQueueSize(long, List)} or {@link
|
||||
* #shouldCancelChunkLoad(long, Chunk, List)} will only happen after this call.
|
||||
*
|
||||
* <p>This method may not be called when the track selection is already enabled.
|
||||
*/
|
||||
@ -102,8 +103,8 @@ public interface TrackSelection {
|
||||
|
||||
/**
|
||||
* Disables this track selection. No further dynamic changes via {@link #updateSelectedTrack(long,
|
||||
* long, long, List, MediaChunkIterator[])} or {@link #evaluateQueueSize(long, List)} will happen
|
||||
* after this call.
|
||||
* long, long, List, MediaChunkIterator[])}, {@link #evaluateQueueSize(long, List)} or {@link
|
||||
* #shouldCancelChunkLoad(long, Chunk, List)} will happen after this call.
|
||||
*
|
||||
* <p>This method may only be called when the track selection is already enabled.
|
||||
*/
|
||||
@ -202,7 +203,7 @@ public interface TrackSelection {
|
||||
/**
|
||||
* Updates the selected track for sources that load media in discrete {@link MediaChunk}s.
|
||||
*
|
||||
* <p>This method may only be called when the selection is enabled.
|
||||
* <p>This method will only be called when the selection is enabled.
|
||||
*
|
||||
* @param playbackPositionUs The current playback position in microseconds. If playback of the
|
||||
* period to which this track selection belongs has not yet started, the value will be the
|
||||
@ -231,34 +232,77 @@ public interface TrackSelection {
|
||||
MediaChunkIterator[] mediaChunkIterators);
|
||||
|
||||
/**
|
||||
* May be called periodically by sources that load media in discrete {@link MediaChunk}s and
|
||||
* support discarding of buffered chunks in order to re-buffer using a different selected track.
|
||||
* Returns the number of chunks that should be retained in the queue.
|
||||
* <p>
|
||||
* To avoid excessive re-buffering, implementations should normally return the size of the queue.
|
||||
* An example of a case where a smaller value may be returned is if network conditions have
|
||||
*
|
||||
* <p>May be called by sources that load media in discrete {@link MediaChunk MediaChunks} and
|
||||
* support discarding of buffered chunks.
|
||||
*
|
||||
* <p>To avoid excessive re-buffering, implementations should normally return the size of the
|
||||
* queue. An example of a case where a smaller value may be returned is if network conditions have
|
||||
* improved dramatically, allowing chunks to be discarded and re-buffered in a track of
|
||||
* significantly higher quality. Discarding chunks may allow faster switching to a higher quality
|
||||
* track in this case. This method may only be called when the selection is enabled.
|
||||
* track in this case.
|
||||
*
|
||||
* <p>Note that even if the source supports discarding of buffered chunks, the actual number of
|
||||
* discarded chunks is not guaranteed. The source will call {@link #updateSelectedTrack(long,
|
||||
* long, long, List, MediaChunkIterator[])} with the updated queue of chunks before loading a new
|
||||
* chunk to allow switching to another quality.
|
||||
*
|
||||
* <p>This method will only be called when the selection is enabled and none of the {@link
|
||||
* MediaChunk MediaChunks} in the queue are currently loading.
|
||||
*
|
||||
* @param playbackPositionUs The current playback position in microseconds. If playback of the
|
||||
* period to which this track selection belongs has not yet started, the value will be the
|
||||
* starting position in the period minus the duration of any media in previous periods still
|
||||
* to be played.
|
||||
* @param queue The queue of buffered {@link MediaChunk}s. Must not be modified.
|
||||
* @param queue The queue of buffered {@link MediaChunk MediaChunks}. Must not be modified.
|
||||
* @return The number of chunks to retain in the queue.
|
||||
*/
|
||||
int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue);
|
||||
|
||||
/**
|
||||
* Returns whether an ongoing load of a chunk should be canceled.
|
||||
*
|
||||
* <p>May be called by sources that load media in discrete {@link MediaChunk MediaChunks} and
|
||||
* support canceling the ongoing chunk load. The ongoing chunk load is either the last {@link
|
||||
* MediaChunk} in the queue or another type of {@link Chunk}, for example, if the source loads
|
||||
* initialization or encryption data.
|
||||
*
|
||||
* <p>To avoid excessive re-buffering, implementations should normally return {@code false}. An
|
||||
* example where {@code true} might be returned is if a load of a high quality chunk gets stuck
|
||||
* and canceling this load in favor of a lower quality alternative may avoid a rebuffer.
|
||||
*
|
||||
* <p>The source will call {@link #evaluateQueueSize(long, List)} after the cancelation finishes
|
||||
* to allow discarding of chunks, and {@link #updateSelectedTrack(long, long, long, List,
|
||||
* MediaChunkIterator[])} before loading a new chunk to allow switching to another quality.
|
||||
*
|
||||
* <p>This method will only be called when the selection is enabled.
|
||||
*
|
||||
* @param playbackPositionUs The current playback position in microseconds. If playback of the
|
||||
* period to which this track selection belongs has not yet started, the value will be the
|
||||
* starting position in the period minus the duration of any media in previous periods still
|
||||
* to be played.
|
||||
* @param loadingChunk The currently loading {@link Chunk} that will be canceled if this method
|
||||
* returns {@code true}.
|
||||
* @param queue The queue of buffered {@link MediaChunk MediaChunks}, including the {@code
|
||||
* loadingChunk} if it's a {@link MediaChunk}. Must not be modified.
|
||||
* @return Whether the ongoing load of {@code loadingChunk} should be canceled.
|
||||
*/
|
||||
default boolean shouldCancelChunkLoad(
|
||||
long playbackPositionUs, Chunk loadingChunk, List<? extends MediaChunk> queue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to blacklist the track at the specified index in the selection, making it ineligible
|
||||
* for selection by calls to {@link #updateSelectedTrack(long, long, long, List,
|
||||
* MediaChunkIterator[])} for the specified period of time. Blacklisting will fail if all other
|
||||
* tracks are currently blacklisted. If blacklisting the currently selected track, note that it
|
||||
* will remain selected until the next call to {@link #updateSelectedTrack(long, long, long, List,
|
||||
* MediaChunkIterator[])}.
|
||||
* MediaChunkIterator[])} for the specified period of time.
|
||||
*
|
||||
* <p>This method may only be called when the selection is enabled.
|
||||
* <p>Blacklisting will fail if all other tracks are currently blacklisted. If blacklisting the
|
||||
* currently selected track, note that it will remain selected until the next call to {@link
|
||||
* #updateSelectedTrack(long, long, long, List, MediaChunkIterator[])}.
|
||||
*
|
||||
* <p>This method will only be called when the selection is enabled.
|
||||
*
|
||||
* @param index The index of the track in the selection.
|
||||
* @param blacklistDurationMs The duration of time for which the track should be blacklisted, in
|
||||
|
@ -458,6 +458,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
* could be replaced with chunks of a significantly higher quality (e.g. because the available
|
||||
* bandwidth has substantially increased).
|
||||
*
|
||||
* <p>Will only be called if no {@link MediaChunk} in the queue is currently loading.
|
||||
*
|
||||
* @param playbackPositionUs The current playback position, in microseconds.
|
||||
* @param queue The queue of buffered {@link MediaChunk MediaChunks}.
|
||||
* @return The preferred queue size.
|
||||
@ -469,6 +471,22 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
return trackSelection.evaluateQueueSize(playbackPositionUs, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an ongoing load of a chunk should be canceled.
|
||||
*
|
||||
* @param playbackPositionUs The current playback position, in microseconds.
|
||||
* @param loadingChunk The currently loading {@link Chunk}.
|
||||
* @param queue The queue of buffered {@link MediaChunk MediaChunks}.
|
||||
* @return Whether the ongoing load of {@code loadingChunk} should be canceled.
|
||||
*/
|
||||
public boolean shouldCancelLoad(
|
||||
long playbackPositionUs, Chunk loadingChunk, List<? extends MediaChunk> queue) {
|
||||
if (fatalError != null) {
|
||||
return false;
|
||||
}
|
||||
return trackSelection.shouldCancelChunkLoad(playbackPositionUs, loadingChunk, queue);
|
||||
}
|
||||
|
||||
// Private methods.
|
||||
|
||||
/**
|
||||
|
@ -133,6 +133,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
private final ArrayList<HlsSampleStream> hlsSampleStreams;
|
||||
private final Map<String, DrmInitData> overridingDrmInitData;
|
||||
|
||||
@Nullable private Chunk loadingChunk;
|
||||
private HlsSampleQueue[] sampleQueues;
|
||||
private int[] sampleQueueTrackIds;
|
||||
private Set<Integer> sampleQueueMappingDoneByType;
|
||||
@ -674,6 +675,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
if (isMediaChunk(loadable)) {
|
||||
initMediaChunkLoad((HlsMediaChunk) loadable);
|
||||
}
|
||||
loadingChunk = loadable;
|
||||
long elapsedRealtimeMs =
|
||||
loader.startLoading(
|
||||
loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type));
|
||||
@ -700,14 +702,16 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
return;
|
||||
}
|
||||
|
||||
int currentQueueSize = mediaChunks.size();
|
||||
int preferredQueueSize = chunkSource.getPreferredQueueSize(positionUs, readOnlyMediaChunks);
|
||||
if (currentQueueSize <= preferredQueueSize) {
|
||||
if (loader.isLoading()) {
|
||||
Assertions.checkNotNull(loadingChunk);
|
||||
if (chunkSource.shouldCancelLoad(positionUs, loadingChunk, readOnlyMediaChunks)) {
|
||||
loader.cancelLoading();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (loader.isLoading()) {
|
||||
loader.cancelLoading();
|
||||
} else {
|
||||
|
||||
int preferredQueueSize = chunkSource.getPreferredQueueSize(positionUs, readOnlyMediaChunks);
|
||||
if (preferredQueueSize < mediaChunks.size()) {
|
||||
discardUpstream(preferredQueueSize);
|
||||
}
|
||||
}
|
||||
@ -716,6 +720,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
|
||||
@Override
|
||||
public void onLoadCompleted(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs) {
|
||||
loadingChunk = null;
|
||||
chunkSource.onChunkLoadCompleted(loadable);
|
||||
LoadEventInfo loadEventInfo =
|
||||
new LoadEventInfo(
|
||||
@ -746,6 +751,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
@Override
|
||||
public void onLoadCanceled(
|
||||
Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, boolean released) {
|
||||
loadingChunk = null;
|
||||
LoadEventInfo loadEventInfo =
|
||||
new LoadEventInfo(
|
||||
loadable.loadTaskId,
|
||||
@ -841,6 +847,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
error,
|
||||
wasCanceled);
|
||||
if (wasCanceled) {
|
||||
loadingChunk = null;
|
||||
loadErrorHandlingPolicy.onLoadTaskConcluded(loadable.loadTaskId);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user