diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 41c5d21f75..0feeb56828 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -49,6 +49,9 @@ `LoadingInfo` contains additional parameters, including `playbackSpeed` and `lastRebufferRealtimeMs` in addition to the existing `playbackPositionUs`. + * Enhance `ChunkSource.getNextChunk(long, long, List, ChunkHolder)` method + in the `ChunkSource` interface to `ChunkSource.getNextChunk(LoadingInfo, + long, List, ChunkHolder)`. * Transformer: * Parse EXIF rotation data for image inputs. * Remove `TransformationRequest.HdrMode` annotation type and its diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java index 4acdbb191f..24b6bd9ff7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java @@ -577,8 +577,7 @@ public class ChunkSampleStream chunkQueue = readOnlyMediaChunks; loadPositionUs = getLastMediaChunk().endTimeUs; } - chunkSource.getNextChunk( - loadingInfo.playbackPositionUs, loadPositionUs, chunkQueue, nextChunkHolder); + chunkSource.getNextChunk(loadingInfo, loadPositionUs, chunkQueue, nextChunkHolder); boolean endOfStream = nextChunkHolder.endOfStream; @Nullable Chunk loadable = nextChunkHolder.chunk; nextChunkHolder.clear(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSource.java index 91d1900d38..05cdf5ff81 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSource.java @@ -16,6 +16,7 @@ package androidx.media3.exoplayer.source.chunk; import androidx.media3.common.util.UnstableApi; +import androidx.media3.exoplayer.LoadingInfo; import androidx.media3.exoplayer.SeekParameters; import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy; import java.io.IOException; @@ -76,10 +77,7 @@ public interface ChunkSource { * been reached then {@link ChunkHolder#endOfStream} is set. If a chunk is not available but the * end of the stream has not been reached, the {@link ChunkHolder} is not modified. * - * @param playbackPositionUs The current playback position in microseconds. If playback of the - * period to which this chunk source 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 loadingInfo The {@link LoadingInfo} when loading request is made. * @param loadPositionUs The current load position in microseconds. If {@code queue} is empty, * this is the starting position from which chunks should be provided. Else it's equal to * {@link MediaChunk#endTimeUs} of the last chunk in the {@code queue}. @@ -87,7 +85,7 @@ public interface ChunkSource { * @param out A holder to populate. */ void getNextChunk( - long playbackPositionUs, + LoadingInfo loadingInfo, long loadPositionUs, List queue, ChunkHolder out); @@ -111,8 +109,8 @@ public interface ChunkSource { * handling the load error. * @return Whether the load should be canceled so that a replacement chunk can be loaded instead. * Must be {@code false} if {@code cancelable} is {@code false}. If {@code true}, {@link - * #getNextChunk(long, long, List, ChunkHolder)} will be called to obtain the replacement - * chunk. + * #getNextChunk(LoadingInfo, long, List, ChunkHolder)} will be called to obtain the + * replacement chunk. */ boolean onChunkLoadError( Chunk chunk, diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java index a2e967503b..1859825f87 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java @@ -30,6 +30,7 @@ import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException; import androidx.media3.datasource.TransferListener; +import androidx.media3.exoplayer.LoadingInfo; import androidx.media3.exoplayer.SeekParameters; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.dash.PlayerEmsgHandler.PlayerTrackEmsgHandler; @@ -315,7 +316,7 @@ public class DefaultDashChunkSource implements DashChunkSource { @Override public void getNextChunk( - long playbackPositionUs, + LoadingInfo loadingInfo, long loadPositionUs, List queue, ChunkHolder out) { @@ -323,6 +324,7 @@ public class DefaultDashChunkSource implements DashChunkSource { return; } + long playbackPositionUs = loadingInfo.playbackPositionUs; long bufferedDurationUs = loadPositionUs - playbackPositionUs; long presentationPositionUs = Util.msToUs(manifest.availabilityStartTimeMs) diff --git a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DefaultDashChunkSourceTest.java b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DefaultDashChunkSourceTest.java index c6387ed254..edfb19c8d5 100644 --- a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DefaultDashChunkSourceTest.java +++ b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DefaultDashChunkSourceTest.java @@ -30,6 +30,7 @@ import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Util; import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.HttpDataSource; +import androidx.media3.exoplayer.LoadingInfo; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.dash.manifest.DashManifest; import androidx.media3.exoplayer.dash.manifest.DashManifestParser; @@ -107,7 +108,9 @@ public class DefaultDashChunkSourceTest { ChunkHolder output = new ChunkHolder(); chunkSource.getNextChunk( - /* playbackPositionUs= */ nowInPeriodUs - 5 * C.MICROS_PER_SECOND, + new LoadingInfo.Builder() + .setPlaybackPositionUs(nowInPeriodUs - 5 * C.MICROS_PER_SECOND) + .build(), /* loadPositionUs= */ nowInPeriodUs - 5 * C.MICROS_PER_SECOND, /* queue= */ ImmutableList.of(), output); @@ -115,7 +118,7 @@ public class DefaultDashChunkSourceTest { .isEqualTo(0); chunkSource.getNextChunk( - /* playbackPositionUs= */ nowInPeriodUs, + new LoadingInfo.Builder().setPlaybackPositionUs(nowInPeriodUs).build(), /* loadPositionUs= */ nowInPeriodUs, /* queue= */ ImmutableList.of(), output); @@ -155,7 +158,7 @@ public class DefaultDashChunkSourceTest { ChunkHolder output = new ChunkHolder(); chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -183,7 +186,7 @@ public class DefaultDashChunkSourceTest { boolean requestReplacementChunk = true; while (requestReplacementChunk) { chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -213,7 +216,7 @@ public class DefaultDashChunkSourceTest { output.chunk.dataSpec, /* httpResponseCode= */ 404, /* errorCount= */ 1), loadErrorHandlingPolicy); chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -241,7 +244,7 @@ public class DefaultDashChunkSourceTest { boolean requestReplacementChunk = true; while (requestReplacementChunk) { chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -280,7 +283,7 @@ public class DefaultDashChunkSourceTest { createDashChunkSource(/* numberOfTracks= */ 2, /* cmcdConfiguration= */ null); ChunkHolder output = new ChunkHolder(); chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -307,7 +310,7 @@ public class DefaultDashChunkSourceTest { ChunkHolder output = new ChunkHolder(); chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -352,7 +355,7 @@ public class DefaultDashChunkSourceTest { ChunkHolder output = new ChunkHolder(); chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -398,7 +401,7 @@ public class DefaultDashChunkSourceTest { ChunkHolder output = new ChunkHolder(); chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -447,7 +450,7 @@ public class DefaultDashChunkSourceTest { ChunkHolder output = new ChunkHolder(); // Populate with last available media chunk chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -456,7 +459,7 @@ public class DefaultDashChunkSourceTest { // Request another chunk chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 4_000_000, /* queue= */ ImmutableList.of((MediaChunk) previousChunk), output); @@ -496,7 +499,7 @@ public class DefaultDashChunkSourceTest { ChunkHolder output = new ChunkHolder(); // Populate with last media chunk chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 4_000_000, /* queue= */ ImmutableList.of(), output); @@ -505,7 +508,7 @@ public class DefaultDashChunkSourceTest { // Request next chunk chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 8_000_000, /* queue= */ ImmutableList.of((MediaChunk) previousChunk), output); diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java index 73e3f39858..a6c111b741 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java @@ -35,6 +35,7 @@ import androidx.media3.common.util.Util; import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.TransferListener; +import androidx.media3.exoplayer.LoadingInfo; import androidx.media3.exoplayer.SeekParameters; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist; @@ -364,10 +365,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * but the end of the stream has not been reached, {@link HlsChunkHolder#playlistUrl} is set to * contain the {@link Uri} that refers to the playlist that needs refreshing. * - * @param playbackPositionUs The current playback position relative to the period start in - * microseconds. If playback of the period to which this chunk source 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 loadingInfo The {@link LoadingInfo} when loading request is made. * @param loadPositionUs The current load position relative to the period start in microseconds. * @param queue The queue of buffered {@link HlsMediaChunk}s. * @param allowEndOfStream Whether {@link HlsChunkHolder#endOfStream} is allowed to be set for @@ -376,13 +374,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * @param out A holder to populate. */ public void getNextChunk( - long playbackPositionUs, + LoadingInfo loadingInfo, long loadPositionUs, List queue, boolean allowEndOfStream, HlsChunkHolder out) { @Nullable HlsMediaChunk previous = queue.isEmpty() ? null : Iterables.getLast(queue); int oldTrackIndex = previous == null ? C.INDEX_UNSET : trackGroup.indexOf(previous.trackFormat); + long playbackPositionUs = loadingInfo.playbackPositionUs; long bufferedDurationUs = loadPositionUs - playbackPositionUs; long timeToLiveEdgeUs = resolveTimeToLiveEdgeUs(playbackPositionUs); if (previous != null && !independentSegments) { 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 9c95d0d885..005ccafda5 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 @@ -761,7 +761,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } nextChunkHolder.clear(); chunkSource.getNextChunk( - loadingInfo.playbackPositionUs, + loadingInfo, loadPositionUs, chunkQueue, /* allowEndOfStream= */ prepared || !chunkQueue.isEmpty(), diff --git a/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/HlsChunkSourceTest.java b/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/HlsChunkSourceTest.java index 0a317d2042..32f7ddbcfc 100644 --- a/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/HlsChunkSourceTest.java +++ b/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/HlsChunkSourceTest.java @@ -26,6 +26,7 @@ import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MimeTypes; +import androidx.media3.exoplayer.LoadingInfo; import androidx.media3.exoplayer.SeekParameters; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist; @@ -201,7 +202,7 @@ public class HlsChunkSourceTest { HlsChunkSource.HlsChunkHolder output = new HlsChunkSource.HlsChunkHolder(); testChunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), /* allowEndOfStream= */ true, @@ -247,7 +248,7 @@ public class HlsChunkSourceTest { HlsChunkSource.HlsChunkHolder output = new HlsChunkSource.HlsChunkHolder(); testChunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), /* allowEndOfStream= */ true, @@ -294,7 +295,7 @@ public class HlsChunkSourceTest { HlsChunkSource.HlsChunkHolder output = new HlsChunkSource.HlsChunkHolder(); testChunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), /* allowEndOfStream= */ true, diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSource.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSource.java index e806c510ef..850ae1983f 100644 --- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSource.java +++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSource.java @@ -26,6 +26,7 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.TransferListener; +import androidx.media3.exoplayer.LoadingInfo; import androidx.media3.exoplayer.SeekParameters; import androidx.media3.exoplayer.smoothstreaming.manifest.SsManifest; import androidx.media3.exoplayer.smoothstreaming.manifest.SsManifest.StreamElement; @@ -224,7 +225,7 @@ public class DefaultSsChunkSource implements SsChunkSource { @Override public final void getNextChunk( - long playbackPositionUs, + LoadingInfo loadingInfo, long loadPositionUs, List queue, ChunkHolder out) { @@ -258,6 +259,7 @@ public class DefaultSsChunkSource implements SsChunkSource { return; } + long playbackPositionUs = loadingInfo.playbackPositionUs; long bufferedDurationUs = loadPositionUs - playbackPositionUs; long timeToLiveEdgeUs = resolveTimeToLiveEdgeUs(playbackPositionUs); diff --git a/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSourceTest.java b/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSourceTest.java index a31784b5a9..574494ed9c 100644 --- a/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSourceTest.java +++ b/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSourceTest.java @@ -23,6 +23,7 @@ import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.TrackGroup; import androidx.media3.common.util.Assertions; +import androidx.media3.exoplayer.LoadingInfo; import androidx.media3.exoplayer.smoothstreaming.manifest.SsManifest; import androidx.media3.exoplayer.smoothstreaming.manifest.SsManifestParser; import androidx.media3.exoplayer.source.chunk.ChunkHolder; @@ -56,7 +57,7 @@ public class DefaultSsChunkSourceTest { ChunkHolder output = new ChunkHolder(); chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -101,7 +102,7 @@ public class DefaultSsChunkSourceTest { ChunkHolder output = new ChunkHolder(); chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); @@ -147,7 +148,7 @@ public class DefaultSsChunkSourceTest { ChunkHolder output = new ChunkHolder(); chunkSource.getNextChunk( - /* playbackPositionUs= */ 0, + new LoadingInfo.Builder().setPlaybackPositionUs(0).build(), /* loadPositionUs= */ 0, /* queue= */ ImmutableList.of(), output); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeChunkSource.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeChunkSource.java index 29660a9780..33f15cc85c 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeChunkSource.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeChunkSource.java @@ -25,6 +25,7 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.TransferListener; +import androidx.media3.exoplayer.LoadingInfo; import androidx.media3.exoplayer.SeekParameters; import androidx.media3.exoplayer.source.chunk.Chunk; import androidx.media3.exoplayer.source.chunk.ChunkHolder; @@ -108,10 +109,11 @@ public class FakeChunkSource implements ChunkSource { @Override public void getNextChunk( - long playbackPositionUs, + LoadingInfo loadingInfo, long loadPositionUs, List queue, ChunkHolder out) { + long playbackPositionUs = loadingInfo.playbackPositionUs; long bufferedDurationUs = loadPositionUs - playbackPositionUs; int chunkIndex = queue.isEmpty()