Allow associating LoadControl methods with the relevant MediaPeriod.
PiperOrigin-RevId: 520037412
This commit is contained in:
parent
1dbb19a44c
commit
f88280dc78
@ -13,6 +13,9 @@
|
||||
`SampleQueue.sourceId` and `SampleQueue.peekSourceId`.
|
||||
* Reset target live stream override when seeking to default position
|
||||
([#11051](https://github.com/google/ExoPlayer/pull/11051)).
|
||||
* Add parameters to `LoadControl` methods `shouldStartPlayback` and
|
||||
`onTracksSelected` that allow associating these methods with the
|
||||
relevant `MediaPeriod`.
|
||||
* Audio:
|
||||
* Fix bug where some playbacks fail when tunneling is enabled and
|
||||
`AudioProcessors` are active, e.g. for gapless trimming
|
||||
|
@ -21,6 +21,8 @@ import static java.lang.Math.min;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaPeriodId;
|
||||
import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.util.Assertions;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
@ -330,7 +332,11 @@ public class DefaultLoadControl implements LoadControl {
|
||||
|
||||
@Override
|
||||
public void onTracksSelected(
|
||||
Renderer[] renderers, TrackGroupArray trackGroups, ExoTrackSelection[] trackSelections) {
|
||||
Timeline timeline,
|
||||
MediaPeriodId mediaPeriodId,
|
||||
Renderer[] renderers,
|
||||
TrackGroupArray trackGroups,
|
||||
ExoTrackSelection[] trackSelections) {
|
||||
targetBufferBytes =
|
||||
targetBufferBytesOverwrite == C.LENGTH_UNSET
|
||||
? calculateTargetBufferBytes(renderers, trackSelections)
|
||||
@ -392,7 +398,12 @@ public class DefaultLoadControl implements LoadControl {
|
||||
|
||||
@Override
|
||||
public boolean shouldStartPlayback(
|
||||
long bufferedDurationUs, float playbackSpeed, boolean rebuffering, long targetLiveOffsetUs) {
|
||||
Timeline timeline,
|
||||
MediaPeriodId mediaPeriodId,
|
||||
long bufferedDurationUs,
|
||||
float playbackSpeed,
|
||||
boolean rebuffering,
|
||||
long targetLiveOffsetUs) {
|
||||
bufferedDurationUs = Util.getPlayoutDurationForMediaDuration(bufferedDurationUs, playbackSpeed);
|
||||
long minBufferDurationUs = rebuffering ? bufferForPlaybackAfterRebufferUs : bufferForPlaybackUs;
|
||||
if (targetLiveOffsetUs != C.TIME_UNSET) {
|
||||
|
@ -1823,8 +1823,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
return true;
|
||||
}
|
||||
// Renderers are ready and we're loading. Ask the LoadControl whether to transition.
|
||||
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
|
||||
long targetLiveOffsetUs =
|
||||
shouldUseLivePlaybackSpeedControl(playbackInfo.timeline, queue.getPlayingPeriod().info.id)
|
||||
shouldUseLivePlaybackSpeedControl(playbackInfo.timeline, playingPeriodHolder.info.id)
|
||||
? livePlaybackSpeedControl.getTargetLiveOffsetUs()
|
||||
: C.TIME_UNSET;
|
||||
MediaPeriodHolder loadingHolder = queue.getLoadingPeriod();
|
||||
@ -1836,6 +1837,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
return isBufferedToEnd
|
||||
|| isAdPendingPreparation
|
||||
|| loadControl.shouldStartPlayback(
|
||||
playbackInfo.timeline,
|
||||
playingPeriodHolder.info.id,
|
||||
getTotalBufferedDurationUs(),
|
||||
mediaClock.getPlaybackParameters().speed,
|
||||
isRebuffering,
|
||||
@ -2291,7 +2294,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
loadingPeriodHolder.handlePrepared(
|
||||
mediaClock.getPlaybackParameters().speed, playbackInfo.timeline);
|
||||
updateLoadControlTrackSelection(
|
||||
loadingPeriodHolder.getTrackGroups(), loadingPeriodHolder.getTrackSelectorResult());
|
||||
loadingPeriodHolder.info.id,
|
||||
loadingPeriodHolder.getTrackGroups(),
|
||||
loadingPeriodHolder.getTrackSelectorResult());
|
||||
if (loadingPeriodHolder == queue.getPlayingPeriod()) {
|
||||
// This is the first prepared period, so update the position and the renderers.
|
||||
resetRendererPosition(loadingPeriodHolder.info.startPositionUs);
|
||||
@ -2576,6 +2581,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
&& loadingMediaPeriodHolder != null
|
||||
&& loadingMediaPeriodHolder.prepared) {
|
||||
updateLoadControlTrackSelection(
|
||||
loadingMediaPeriodHolder.info.id,
|
||||
loadingMediaPeriodHolder.getTrackGroups(),
|
||||
loadingMediaPeriodHolder.getTrackSelectorResult());
|
||||
}
|
||||
@ -2596,8 +2602,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
}
|
||||
|
||||
private void updateLoadControlTrackSelection(
|
||||
TrackGroupArray trackGroups, TrackSelectorResult trackSelectorResult) {
|
||||
loadControl.onTracksSelected(renderers, trackGroups, trackSelectorResult.selections);
|
||||
MediaPeriodId mediaPeriodId,
|
||||
TrackGroupArray trackGroups,
|
||||
TrackSelectorResult trackSelectorResult) {
|
||||
loadControl.onTracksSelected(
|
||||
playbackInfo.timeline,
|
||||
mediaPeriodId,
|
||||
renderers,
|
||||
trackGroups,
|
||||
trackSelectorResult.selections);
|
||||
}
|
||||
|
||||
private boolean shouldPlayWhenReady() {
|
||||
|
@ -16,9 +16,11 @@
|
||||
package androidx.media3.exoplayer;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaPeriodId;
|
||||
import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.TrackGroup;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||
import androidx.media3.exoplayer.upstream.Allocator;
|
||||
@ -27,18 +29,49 @@ import androidx.media3.exoplayer.upstream.Allocator;
|
||||
@UnstableApi
|
||||
public interface LoadControl {
|
||||
|
||||
/**
|
||||
* @deprecated Used as a placeholder when MediaPeriodId is unknown. Only used when the deprecated
|
||||
* methods {@link #onTracksSelected(Renderer[], TrackGroupArray, ExoTrackSelection[])} or
|
||||
* {@link #shouldStartPlayback(long, float, boolean, long)} are called.
|
||||
*/
|
||||
@Deprecated
|
||||
MediaPeriodId EMPTY_MEDIA_PERIOD_ID = new MediaPeriodId(/* periodUid= */ new Object());
|
||||
|
||||
/** Called by the player when prepared with a new source. */
|
||||
void onPrepared();
|
||||
|
||||
/**
|
||||
* Called by the player when a track selection occurs.
|
||||
*
|
||||
* @param timeline The current {@link Timeline} in ExoPlayer. Can be {@link Timeline#EMPTY} only
|
||||
* when the deprecated {@link #onTracksSelected(Renderer[], TrackGroupArray,
|
||||
* ExoTrackSelection[])} was called.
|
||||
* @param mediaPeriodId Identifies (in the current timeline) the {@link MediaPeriod} for which the
|
||||
* selection was made. Will be {@link #EMPTY_MEDIA_PERIOD_ID} when {@code timeline} is empty.
|
||||
* @param renderers The renderers.
|
||||
* @param trackGroups The {@link TrackGroup}s from which the selection was made.
|
||||
* @param trackSelections The track selections that were made.
|
||||
*/
|
||||
void onTracksSelected(
|
||||
Renderer[] renderers, TrackGroupArray trackGroups, ExoTrackSelection[] trackSelections);
|
||||
@SuppressWarnings("deprecation") // Calling deprecated version of this method.
|
||||
default void onTracksSelected(
|
||||
Timeline timeline,
|
||||
MediaPeriodId mediaPeriodId,
|
||||
Renderer[] renderers,
|
||||
TrackGroupArray trackGroups,
|
||||
ExoTrackSelection[] trackSelections) {
|
||||
onTracksSelected(renderers, trackGroups, trackSelections);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Implement {@link #onTracksSelected(Timeline, MediaPeriodId, Renderer[],
|
||||
* TrackGroupArray, ExoTrackSelection[])} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
default void onTracksSelected(
|
||||
Renderer[] renderers, TrackGroupArray trackGroups, ExoTrackSelection[] trackSelections) {
|
||||
onTracksSelected(
|
||||
Timeline.EMPTY, EMPTY_MEDIA_PERIOD_ID, renderers, trackGroups, trackSelections);
|
||||
}
|
||||
|
||||
/** Called by the player when stopped. */
|
||||
void onStopped();
|
||||
@ -84,7 +117,9 @@ public interface LoadControl {
|
||||
boolean retainBackBufferFromKeyframe();
|
||||
|
||||
/**
|
||||
* Called by the player to determine whether it should continue to load the source.
|
||||
* Called by the player to determine whether it should continue to load the source. If this method
|
||||
* returns true, the {@link MediaPeriod} identified in the most recent {@link #onTracksSelected}
|
||||
* call will continue being loaded.
|
||||
*
|
||||
* @param playbackPositionUs The current playback position in microseconds, relative to the start
|
||||
* of the {@link Timeline.Period period} that will continue to be loaded if this method
|
||||
@ -104,6 +139,10 @@ public interface LoadControl {
|
||||
* determines whether playback is actually started. The load control may opt to return {@code
|
||||
* false} until some condition has been met (e.g. a certain amount of media is buffered).
|
||||
*
|
||||
* @param timeline The current {@link Timeline} in ExoPlayer. Can be {@link Timeline#EMPTY} only
|
||||
* when the deprecated {@link #shouldStartPlayback(long, float, boolean, long)} was called.
|
||||
* @param mediaPeriodId Identifies (in the current timeline) the {@link MediaPeriod} for which
|
||||
* playback will start. Will be {@link #EMPTY_MEDIA_PERIOD_ID} when {@code timeline} is empty.
|
||||
* @param bufferedDurationUs The duration of media that's currently buffered.
|
||||
* @param playbackSpeed The current factor by which playback is sped up.
|
||||
* @param rebuffering Whether the player is rebuffering. A rebuffer is defined to be caused by
|
||||
@ -114,6 +153,30 @@ public interface LoadControl {
|
||||
* configured.
|
||||
* @return Whether playback should be allowed to start or resume.
|
||||
*/
|
||||
boolean shouldStartPlayback(
|
||||
long bufferedDurationUs, float playbackSpeed, boolean rebuffering, long targetLiveOffsetUs);
|
||||
@SuppressWarnings("deprecation") // Calling deprecated version of this method.
|
||||
default boolean shouldStartPlayback(
|
||||
Timeline timeline,
|
||||
MediaPeriodId mediaPeriodId,
|
||||
long bufferedDurationUs,
|
||||
float playbackSpeed,
|
||||
boolean rebuffering,
|
||||
long targetLiveOffsetUs) {
|
||||
return shouldStartPlayback(bufferedDurationUs, playbackSpeed, rebuffering, targetLiveOffsetUs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Implement {@link #shouldStartPlayback(Timeline, MediaPeriodId, long, float,
|
||||
* boolean, long)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean shouldStartPlayback(
|
||||
long bufferedDurationUs, float playbackSpeed, boolean rebuffering, long targetLiveOffsetUs) {
|
||||
return shouldStartPlayback(
|
||||
Timeline.EMPTY,
|
||||
EMPTY_MEDIA_PERIOD_ID,
|
||||
bufferedDurationUs,
|
||||
playbackSpeed,
|
||||
rebuffering,
|
||||
targetLiveOffsetUs);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package androidx.media3.exoplayer;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.exoplayer.DefaultLoadControl.Builder;
|
||||
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||
@ -179,7 +180,12 @@ public class DefaultLoadControlTest {
|
||||
@Test
|
||||
public void shouldContinueLoading_withNoSelectedTracks_returnsTrue() {
|
||||
loadControl = builder.build();
|
||||
loadControl.onTracksSelected(new Renderer[0], TrackGroupArray.EMPTY, new ExoTrackSelection[0]);
|
||||
loadControl.onTracksSelected(
|
||||
Timeline.EMPTY,
|
||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
||||
new Renderer[0],
|
||||
TrackGroupArray.EMPTY,
|
||||
new ExoTrackSelection[0]);
|
||||
|
||||
assertThat(
|
||||
loadControl.shouldContinueLoading(
|
||||
@ -203,6 +209,8 @@ public class DefaultLoadControlTest {
|
||||
|
||||
assertThat(
|
||||
loadControl.shouldStartPlayback(
|
||||
Timeline.EMPTY,
|
||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
||||
MIN_BUFFER_US,
|
||||
SPEED,
|
||||
/* rebuffering= */ false,
|
||||
@ -222,6 +230,8 @@ public class DefaultLoadControlTest {
|
||||
|
||||
assertThat(
|
||||
loadControl.shouldStartPlayback(
|
||||
Timeline.EMPTY,
|
||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
||||
/* bufferedDurationUs= */ 2_999_999,
|
||||
SPEED,
|
||||
/* rebuffering= */ false,
|
||||
@ -229,6 +239,8 @@ public class DefaultLoadControlTest {
|
||||
.isFalse();
|
||||
assertThat(
|
||||
loadControl.shouldStartPlayback(
|
||||
Timeline.EMPTY,
|
||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
||||
/* bufferedDurationUs= */ 3_000_000,
|
||||
SPEED,
|
||||
/* rebuffering= */ false,
|
||||
@ -247,6 +259,8 @@ public class DefaultLoadControlTest {
|
||||
|
||||
assertThat(
|
||||
loadControl.shouldStartPlayback(
|
||||
Timeline.EMPTY,
|
||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
||||
/* bufferedDurationUs= */ 499_999,
|
||||
SPEED,
|
||||
/* rebuffering= */ true,
|
||||
@ -254,6 +268,8 @@ public class DefaultLoadControlTest {
|
||||
.isFalse();
|
||||
assertThat(
|
||||
loadControl.shouldStartPlayback(
|
||||
Timeline.EMPTY,
|
||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
||||
/* bufferedDurationUs= */ 500_000,
|
||||
SPEED,
|
||||
/* rebuffering= */ true,
|
||||
@ -273,6 +289,8 @@ public class DefaultLoadControlTest {
|
||||
|
||||
assertThat(
|
||||
loadControl.shouldStartPlayback(
|
||||
Timeline.EMPTY,
|
||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
||||
/* bufferedDurationUs= */ 3_999_999,
|
||||
SPEED,
|
||||
/* rebuffering= */ true,
|
||||
@ -280,6 +298,8 @@ public class DefaultLoadControlTest {
|
||||
.isFalse();
|
||||
assertThat(
|
||||
loadControl.shouldStartPlayback(
|
||||
Timeline.EMPTY,
|
||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
||||
/* bufferedDurationUs= */ 4_000_000,
|
||||
SPEED,
|
||||
/* rebuffering= */ true,
|
||||
@ -298,6 +318,8 @@ public class DefaultLoadControlTest {
|
||||
|
||||
assertThat(
|
||||
loadControl.shouldStartPlayback(
|
||||
Timeline.EMPTY,
|
||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
||||
/* bufferedDurationUs= */ 499_999,
|
||||
SPEED,
|
||||
/* rebuffering= */ true,
|
||||
@ -305,6 +327,8 @@ public class DefaultLoadControlTest {
|
||||
.isFalse();
|
||||
assertThat(
|
||||
loadControl.shouldStartPlayback(
|
||||
Timeline.EMPTY,
|
||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
||||
/* bufferedDurationUs= */ 500_000,
|
||||
SPEED,
|
||||
/* rebuffering= */ true,
|
||||
@ -315,7 +339,8 @@ public class DefaultLoadControlTest {
|
||||
private void build() {
|
||||
builder.setAllocator(allocator).setTargetBufferBytes(TARGET_BUFFER_BYTES);
|
||||
loadControl = builder.build();
|
||||
loadControl.onTracksSelected(new Renderer[0], null, null);
|
||||
loadControl.onTracksSelected(
|
||||
Timeline.EMPTY, LoadControl.EMPTY_MEDIA_PERIOD_ID, new Renderer[0], null, null);
|
||||
}
|
||||
|
||||
private void makeSureTargetBufferBytesReached() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user