From f139d709c7eacc99edb2912126746b6c6d44ae2f Mon Sep 17 00:00:00 2001 From: tianyifeng Date: Tue, 13 Aug 2024 09:43:07 -0700 Subject: [PATCH] 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 PiperOrigin-RevId: 662550622 (cherry picked from commit cd532c5fb290a6f02906a450b06b8bcff3253eb1) --- RELEASENOTES.md | 2 + .../source/preload/PreloadMediaSource.java | 115 ++++++++++-------- 2 files changed, 64 insertions(+), 53 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e241858bef7..9aecd342b49 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,6 +4,8 @@ * Common Library: * ExoPlayer: + * Handle preload callbacks asynchronously in `PreloadMediaSource` + ([#1568](https://github.com/androidx/media/issues/1568)). * Transformer: * Track Selection: * Extractors: diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/PreloadMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/PreloadMediaSource.java index ebc199900f1..bfdaaeb20c1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/PreloadMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/PreloadMediaSource.java @@ -293,25 +293,28 @@ protected void prepareSourceInternal() { protected void onChildSourceInfoRefreshed(Timeline newTimeline) { this.timeline = newTimeline; refreshSourceInfo(newTimeline); - if (isUsedByPlayer() || onSourcePreparedNotified) { - return; - } - onSourcePreparedNotified = true; - if (!preloadControl.onSourcePrepared(this)) { - return; - } - Pair periodPosition = - newTimeline.getPeriodPositionUs( - new Timeline.Window(), - new Timeline.Period(), - /* windowIndex= */ 0, - /* windowPositionUs= */ startPositionUs); - MediaPeriodId mediaPeriodId = new MediaPeriodId(periodPosition.first); - PreloadMediaPeriod mediaPeriod = - PreloadMediaSource.this.createPeriod(mediaPeriodId, allocator, periodPosition.second); - mediaPeriod.preload( - new PreloadMediaPeriodCallback(periodPosition.second), - /* positionUs= */ periodPosition.second); + preloadHandler.post( + () -> { + if (isUsedByPlayer() || onSourcePreparedNotified) { + return; + } + onSourcePreparedNotified = true; + if (!preloadControl.onSourcePrepared(this)) { + return; + } + Pair periodPosition = + newTimeline.getPeriodPositionUs( + new Timeline.Window(), + new Timeline.Period(), + /* windowIndex= */ 0, + /* windowPositionUs= */ startPositionUs); + MediaPeriodId mediaPeriodId = new MediaPeriodId(periodPosition.first); + PreloadMediaPeriod mediaPeriod = + PreloadMediaSource.this.createPeriod(mediaPeriodId, allocator, periodPosition.second); + mediaPeriod.preload( + new PreloadMediaPeriodCallback(periodPosition.second), + /* positionUs= */ periodPosition.second); + }); } @Override @@ -405,44 +408,50 @@ public PreloadMediaPeriodCallback(long periodStartPositionUs) { @Override public void onPrepared(MediaPeriod mediaPeriod) { prepared = true; - if (isUsedByPlayer()) { - return; - } - PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod; - TrackGroupArray trackGroups = preloadMediaPeriod.getTrackGroups(); - @Nullable TrackSelectorResult trackSelectorResult = null; - MediaPeriodKey key = checkNotNull(preloadingMediaPeriodAndKey).second; - try { - trackSelectorResult = - trackSelector.selectTracks( - rendererCapabilities, trackGroups, key.mediaPeriodId, checkNotNull(timeline)); - } catch (ExoPlaybackException e) { - Log.e(TAG, "Failed to select tracks", e); - } - if (trackSelectorResult != null) { - preloadMediaPeriod.selectTracksForPreloading( - trackSelectorResult.selections, periodStartPositionUs); - if (preloadControl.onTracksSelected(PreloadMediaSource.this)) { - preloadMediaPeriod.continueLoading( - new LoadingInfo.Builder().setPlaybackPositionUs(periodStartPositionUs).build()); - } - } + preloadHandler.post( + () -> { + if (isUsedByPlayer()) { + return; + } + PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod; + TrackGroupArray trackGroups = preloadMediaPeriod.getTrackGroups(); + @Nullable TrackSelectorResult trackSelectorResult = null; + MediaPeriodKey key = checkNotNull(preloadingMediaPeriodAndKey).second; + try { + trackSelectorResult = + trackSelector.selectTracks( + rendererCapabilities, trackGroups, key.mediaPeriodId, checkNotNull(timeline)); + } catch (ExoPlaybackException e) { + Log.e(TAG, "Failed to select tracks", e); + } + if (trackSelectorResult != null) { + preloadMediaPeriod.selectTracksForPreloading( + trackSelectorResult.selections, periodStartPositionUs); + if (preloadControl.onTracksSelected(PreloadMediaSource.this)) { + preloadMediaPeriod.continueLoading( + new LoadingInfo.Builder().setPlaybackPositionUs(periodStartPositionUs).build()); + } + } + }); } @Override public void onContinueLoadingRequested(MediaPeriod mediaPeriod) { - if (isUsedByPlayer()) { - return; - } - PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod; - if (prepared && mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE) { - preloadControl.onLoadedToTheEndOfSource(PreloadMediaSource.this); - } else if (!prepared - || preloadControl.onContinueLoadingRequested( - PreloadMediaSource.this, preloadMediaPeriod.getBufferedPositionUs())) { - preloadMediaPeriod.continueLoading( - new LoadingInfo.Builder().setPlaybackPositionUs(periodStartPositionUs).build()); - } + preloadHandler.post( + () -> { + if (isUsedByPlayer()) { + return; + } + PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod; + if (prepared && mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE) { + preloadControl.onLoadedToTheEndOfSource(PreloadMediaSource.this); + } else if (!prepared + || preloadControl.onContinueLoadingRequested( + PreloadMediaSource.this, preloadMediaPeriod.getBufferedPositionUs())) { + preloadMediaPeriod.continueLoading( + new LoadingInfo.Builder().setPlaybackPositionUs(periodStartPositionUs).build()); + } + }); } }