Handle preload callbacks asynchronously in PreloadMediaSource

When there is an exception thrown from the `LoadTask`, the `Loader` will call `Loader.Callback.onLoadError`. Some implementations of `onLoadError` method may call `MediaPeriod.onContinueLoadingRequested`, and in the `PreloadMediaSource`, its `PreloadMediaPeriodCallback` will be triggered and then it can further call `continueLoading` if it finds needed. However the above process is currently done synchronously, which will cause problem. By calling `continueLoading`, the `Loader` is set with a `currentTask`, and when that long sync logic in `Loader.Callback.onLoadError` ends, the `Loader` will immediately retry, and then a non-null `currentTask` will cause the `IllegalStateException`.

Issue: androidx/media#1568

#cherrypick

PiperOrigin-RevId: 662550622
This commit is contained in:
tianyifeng 2024-08-13 09:43:07 -07:00 committed by Copybara-Service
parent 0b23285bae
commit cd532c5fb2
2 changed files with 74 additions and 63 deletions

View File

@ -32,6 +32,8 @@
zero. zero.
* Allow the user to select the built-in speaker for playback on Wear OS * Allow the user to select the built-in speaker for playback on Wear OS
API 35+ (where the device advertises support for this). API 35+ (where the device advertises support for this).
* Handle preload callbacks asynchronously in `PreloadMediaSource`
([#1568](https://github.com/androidx/media/issues/1568)).
* Transformer: * Transformer:
* Add `SurfaceAssetLoader`, which supports queueing video data to * Add `SurfaceAssetLoader`, which supports queueing video data to
Transformer via a `Surface`. Transformer via a `Surface`.

View File

@ -306,26 +306,29 @@ public final class PreloadMediaSource extends WrappingMediaSource {
protected void onChildSourceInfoRefreshed(Timeline newTimeline) { protected void onChildSourceInfoRefreshed(Timeline newTimeline) {
this.timeline = newTimeline; this.timeline = newTimeline;
refreshSourceInfo(newTimeline); refreshSourceInfo(newTimeline);
if (isUsedByPlayer() || onSourcePreparedNotified) { preloadHandler.post(
return; () -> {
} if (isUsedByPlayer() || onSourcePreparedNotified) {
onSourcePreparedNotified = true; return;
if (!preloadControl.onSourcePrepared(this)) { }
stopPreloading(); onSourcePreparedNotified = true;
return; if (!preloadControl.onSourcePrepared(this)) {
} stopPreloading();
Pair<Object, Long> periodPosition = return;
newTimeline.getPeriodPositionUs( }
new Timeline.Window(), Pair<Object, Long> periodPosition =
new Timeline.Period(), newTimeline.getPeriodPositionUs(
/* windowIndex= */ 0, new Timeline.Window(),
/* windowPositionUs= */ startPositionUs); new Timeline.Period(),
MediaPeriodId mediaPeriodId = new MediaPeriodId(periodPosition.first); /* windowIndex= */ 0,
PreloadMediaPeriod mediaPeriod = /* windowPositionUs= */ startPositionUs);
PreloadMediaSource.this.createPeriod(mediaPeriodId, allocator, periodPosition.second); MediaPeriodId mediaPeriodId = new MediaPeriodId(periodPosition.first);
mediaPeriod.preload( PreloadMediaPeriod mediaPeriod =
new PreloadMediaPeriodCallback(periodPosition.second), PreloadMediaSource.this.createPeriod(mediaPeriodId, allocator, periodPosition.second);
/* positionUs= */ periodPosition.second); mediaPeriod.preload(
new PreloadMediaPeriodCallback(periodPosition.second),
/* positionUs= */ periodPosition.second);
});
} }
@Override @Override
@ -455,53 +458,59 @@ public final class PreloadMediaSource extends WrappingMediaSource {
@Override @Override
public void onPrepared(MediaPeriod mediaPeriod) { public void onPrepared(MediaPeriod mediaPeriod) {
prepared = true; prepared = true;
if (isUsedByPlayer()) { preloadHandler.post(
return; () -> {
} if (isUsedByPlayer()) {
PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod; return;
TrackGroupArray trackGroups = preloadMediaPeriod.getTrackGroups(); }
@Nullable TrackSelectorResult trackSelectorResult = null; PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod;
MediaPeriodKey key = checkNotNull(preloadingMediaPeriodAndKey).second; TrackGroupArray trackGroups = preloadMediaPeriod.getTrackGroups();
try { @Nullable TrackSelectorResult trackSelectorResult = null;
trackSelectorResult = MediaPeriodKey key = checkNotNull(preloadingMediaPeriodAndKey).second;
trackSelector.selectTracks( try {
rendererCapabilities, trackGroups, key.mediaPeriodId, checkNotNull(timeline)); trackSelectorResult =
} catch (ExoPlaybackException e) { trackSelector.selectTracks(
Log.e(TAG, "Failed to select tracks", e); rendererCapabilities, trackGroups, key.mediaPeriodId, checkNotNull(timeline));
} } catch (ExoPlaybackException e) {
if (trackSelectorResult == null) { Log.e(TAG, "Failed to select tracks", e);
stopPreloading(); }
return; if (trackSelectorResult == null) {
} stopPreloading();
preloadMediaPeriod.selectTracksForPreloading( return;
trackSelectorResult.selections, periodStartPositionUs); }
if (!preloadControl.onTracksSelected(PreloadMediaSource.this)) { preloadMediaPeriod.selectTracksForPreloading(
stopPreloading(); trackSelectorResult.selections, periodStartPositionUs);
return; if (!preloadControl.onTracksSelected(PreloadMediaSource.this)) {
} stopPreloading();
preloadMediaPeriod.continueLoading( return;
new LoadingInfo.Builder().setPlaybackPositionUs(periodStartPositionUs).build()); }
preloadMediaPeriod.continueLoading(
new LoadingInfo.Builder().setPlaybackPositionUs(periodStartPositionUs).build());
});
} }
@Override @Override
public void onContinueLoadingRequested(MediaPeriod mediaPeriod) { public void onContinueLoadingRequested(MediaPeriod mediaPeriod) {
if (isUsedByPlayer()) { preloadHandler.post(
return; () -> {
} if (isUsedByPlayer()) {
PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod; return;
if (prepared && mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE) { }
preloadControl.onLoadedToTheEndOfSource(PreloadMediaSource.this); PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod;
stopPreloading(); if (prepared && mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE) {
return; preloadControl.onLoadedToTheEndOfSource(PreloadMediaSource.this);
} stopPreloading();
if (prepared return;
&& !preloadControl.onContinueLoadingRequested( }
PreloadMediaSource.this, preloadMediaPeriod.getBufferedPositionUs())) { if (prepared
stopPreloading(); && !preloadControl.onContinueLoadingRequested(
return; PreloadMediaSource.this, preloadMediaPeriod.getBufferedPositionUs())) {
} stopPreloading();
preloadMediaPeriod.continueLoading( return;
new LoadingInfo.Builder().setPlaybackPositionUs(periodStartPositionUs).build()); }
preloadMediaPeriod.continueLoading(
new LoadingInfo.Builder().setPlaybackPositionUs(periodStartPositionUs).build());
});
} }
} }