Enhance getNextChunk() API to allow passing more information

Modifies the `ChunkSource.getNextChunk(long, long, List, ChunkHolder)` method in the `ChunkSource` interface to `ChunkSource.getNextChunk(LoadingInfo, long, List, ChunkHolder)`.

LoadingInfo contains additional parameters, including `playbackRate` and `lastRebufferRealtimeMs` in addition to the existing `playbackPositionUs`. The additional parameters will allow us to pass these information to Common Media Client Data (CMCD) logging.

PiperOrigin-RevId: 555148768
This commit is contained in:
rohks 2023-08-09 13:53:09 +00:00 committed by Tianyi Feng
parent bf5c4d8078
commit cf9f048a3d
11 changed files with 48 additions and 38 deletions

View File

@ -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

View File

@ -577,8 +577,7 @@ public class ChunkSampleStream<T extends ChunkSource>
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();

View File

@ -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<? extends MediaChunk> 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,

View File

@ -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<? extends MediaChunk> 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)

View File

@ -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);

View File

@ -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<HlsMediaChunk> 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) {

View File

@ -761,7 +761,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
}
nextChunkHolder.clear();
chunkSource.getNextChunk(
loadingInfo.playbackPositionUs,
loadingInfo,
loadPositionUs,
chunkQueue,
/* allowEndOfStream= */ prepared || !chunkQueue.isEmpty(),

View File

@ -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,

View File

@ -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<? extends MediaChunk> 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);

View File

@ -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);

View File

@ -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<? extends MediaChunk> queue,
ChunkHolder out) {
long playbackPositionUs = loadingInfo.playbackPositionUs;
long bufferedDurationUs = loadPositionUs - playbackPositionUs;
int chunkIndex =
queue.isEmpty()