diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 07a96bf755..3d9bfed2e4 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -43,6 +43,8 @@ delegated in `HlsSampleStreamWrapper` with an incorrect offset causing an `IndexOutOfBoundsException` or an `IllegalArgumentException` ([#1002](https://github.com/androidx/media/issues/1002)). + * Fix bug where non-primary playlists keep reloading for LL-HLS streams + ([#1240](https://github.com/androidx/media/issues/1240)). * DASH Extension: * Smooth Streaming Extension: * RTSP Extension: diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java index b9961c603c..80c2212cb2 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer.hls; +import static androidx.media3.exoplayer.hls.HlsChunkSource.CHUNK_PUBLICATION_STATE_PRELOAD; import static androidx.media3.exoplayer.hls.HlsChunkSource.CHUNK_PUBLICATION_STATE_PUBLISHED; import static androidx.media3.exoplayer.hls.HlsChunkSource.CHUNK_PUBLICATION_STATE_REMOVED; import static androidx.media3.exoplayer.trackselection.TrackSelectionUtil.createFallbackOptions; @@ -111,7 +112,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; /** * Called to schedule a {@link #continueLoading(LoadingInfo)} call when the playlist referred by - * the given url changes. + * the given url changes, or it requires a refresh to check whether the hinted resource has been + * published or removed. + * + *
Note: This method will be called on a later handler loop than the one on which {@link
+ * #onPlaylistUpdated()} is invoked.
*/
void onPlaylistRefreshRequired(Uri playlistUrl);
}
@@ -543,6 +548,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
int chunkState = chunkSource.getChunkPublicationState(lastMediaChunk);
if (chunkState == CHUNK_PUBLICATION_STATE_PUBLISHED) {
lastMediaChunk.publish();
+ } else if (chunkState == CHUNK_PUBLICATION_STATE_PRELOAD) {
+ handler.post(() -> callback.onPlaylistRefreshRequired(lastMediaChunk.playlistUrl));
} else if (chunkState == CHUNK_PUBLICATION_STATE_REMOVED
&& !loadingFinished
&& loader.isLoading()) {
diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTracker.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTracker.java
index 1cf9fc71e9..98d7c0c53e 100644
--- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTracker.java
+++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTracker.java
@@ -763,13 +763,10 @@ public final class DefaultHlsPlaylistTracker
}
earliestNextLoadTimeMs =
currentTimeMs + Util.usToMs(durationUntilNextLoadUs) - loadEventInfo.loadDurationMs;
- // Schedule a load if this is the primary playlist or a playlist of a low-latency stream and
- // it doesn't have an end tag. Else the next load will be scheduled when refreshPlaylist is
- // called, or when this playlist becomes the primary.
- boolean scheduleLoad =
- playlistSnapshot.partTargetDurationUs != C.TIME_UNSET
- || playlistUrl.equals(primaryMediaPlaylistUrl);
- if (scheduleLoad && !playlistSnapshot.hasEndTag) {
+ // Schedule a load if this is the primary playlist and it doesn't have an end tag. Else the
+ // next load will be scheduled when refreshPlaylist is called, or when this playlist becomes
+ // the primary.
+ if (playlistUrl.equals(primaryMediaPlaylistUrl) && !playlistSnapshot.hasEndTag) {
loadPlaylistInternal(getMediaPlaylistUriForReload());
}
}
diff --git a/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTrackerTest.java b/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTrackerTest.java
index cc537c9c00..00be7e3c34 100644
--- a/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTrackerTest.java
+++ b/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTrackerTest.java
@@ -22,6 +22,7 @@ import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DefaultHttpDataSource;
import androidx.media3.exoplayer.source.MediaSourceEventListener;
import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy;
+import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
import androidx.media3.test.utils.TestUtil;
import androidx.media3.test.utils.robolectric.RobolectricUtil;
import androidx.test.core.app.ApplicationProvider;
@@ -31,6 +32,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
@@ -365,6 +367,85 @@ public class DefaultHlsPlaylistTrackerTest {
assertThat(mediaPlaylists.get(1).trailingParts).hasSize(2);
}
+ @Test
+ public void start_lowLatencyNotScheduleReloadForNonPrimaryPlaylist() throws Exception {
+ List