Commit effbc22a by tonihei Committed by Andrew Lewis

Increase target live offset when rebuffering.

Issue: #4904
PiperOrigin-RevId: 340654178
parent 2416d998
...@@ -33,6 +33,10 @@ import com.google.android.exoplayer2.util.Util; ...@@ -33,6 +33,10 @@ import com.google.android.exoplayer2.util.Util;
* fallback values set with {@link Builder#setFallbackMinPlaybackSpeed(float)} and {@link * fallback values set with {@link Builder#setFallbackMinPlaybackSpeed(float)} and {@link
* Builder#setFallbackMaxPlaybackSpeed(float)} or the {@link #DEFAULT_FALLBACK_MIN_PLAYBACK_SPEED * Builder#setFallbackMaxPlaybackSpeed(float)} or the {@link #DEFAULT_FALLBACK_MIN_PLAYBACK_SPEED
* minimum} and {@link #DEFAULT_FALLBACK_MAX_PLAYBACK_SPEED maximum} fallback default values. * minimum} and {@link #DEFAULT_FALLBACK_MAX_PLAYBACK_SPEED maximum} fallback default values.
*
* <p>When the player rebuffers, the target live offset {@link
* Builder#setTargetLiveOffsetIncrementOnRebufferMs(long) is increased} to adjust to the reduced
* network capabilities.
*/ */
public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedControl { public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedControl {
...@@ -61,6 +65,12 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC ...@@ -61,6 +65,12 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
public static final float DEFAULT_PROPORTIONAL_CONTROL_FACTOR = 0.05f; public static final float DEFAULT_PROPORTIONAL_CONTROL_FACTOR = 0.05f;
/** /**
* The default increment applied to the target live offset each time the player is rebuffering, in
* milliseconds
*/
public static final long DEFAULT_TARGET_LIVE_OFFSET_INCREMENT_ON_REBUFFER_MS = 500;
/**
* The maximum difference between the current live offset and the target live offset for which * The maximum difference between the current live offset and the target live offset for which
* unit speed (1.0f) is used. * unit speed (1.0f) is used.
*/ */
...@@ -73,6 +83,7 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC ...@@ -73,6 +83,7 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
private float fallbackMaxPlaybackSpeed; private float fallbackMaxPlaybackSpeed;
private long minUpdateIntervalMs; private long minUpdateIntervalMs;
private float proportionalControlFactorUs; private float proportionalControlFactorUs;
private long targetLiveOffsetIncrementOnRebufferUs;
/** Creates a builder. */ /** Creates a builder. */
public Builder() { public Builder() {
...@@ -80,6 +91,8 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC ...@@ -80,6 +91,8 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
fallbackMaxPlaybackSpeed = DEFAULT_FALLBACK_MAX_PLAYBACK_SPEED; fallbackMaxPlaybackSpeed = DEFAULT_FALLBACK_MAX_PLAYBACK_SPEED;
minUpdateIntervalMs = DEFAULT_MIN_UPDATE_INTERVAL_MS; minUpdateIntervalMs = DEFAULT_MIN_UPDATE_INTERVAL_MS;
proportionalControlFactorUs = DEFAULT_PROPORTIONAL_CONTROL_FACTOR / C.MICROS_PER_SECOND; proportionalControlFactorUs = DEFAULT_PROPORTIONAL_CONTROL_FACTOR / C.MICROS_PER_SECOND;
targetLiveOffsetIncrementOnRebufferUs =
C.msToUs(DEFAULT_TARGET_LIVE_OFFSET_INCREMENT_ON_REBUFFER_MS);
} }
/** /**
...@@ -145,13 +158,29 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC ...@@ -145,13 +158,29 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
return this; return this;
} }
/**
* Sets the increment applied to the target live offset each time the player is rebuffering, in
* milliseconds.
*
* @param targetLiveOffsetIncrementOnRebufferMs The increment applied to the target live offset
* when the player is rebuffering, in milliseconds
* @return This builder, for convenience.
*/
public Builder setTargetLiveOffsetIncrementOnRebufferMs(
long targetLiveOffsetIncrementOnRebufferMs) {
Assertions.checkArgument(targetLiveOffsetIncrementOnRebufferMs >= 0);
this.targetLiveOffsetIncrementOnRebufferUs = C.msToUs(targetLiveOffsetIncrementOnRebufferMs);
return this;
}
/** Builds an instance. */ /** Builds an instance. */
public DefaultLivePlaybackSpeedControl build() { public DefaultLivePlaybackSpeedControl build() {
return new DefaultLivePlaybackSpeedControl( return new DefaultLivePlaybackSpeedControl(
fallbackMinPlaybackSpeed, fallbackMinPlaybackSpeed,
fallbackMaxPlaybackSpeed, fallbackMaxPlaybackSpeed,
minUpdateIntervalMs, minUpdateIntervalMs,
proportionalControlFactorUs); proportionalControlFactorUs,
targetLiveOffsetIncrementOnRebufferUs);
} }
} }
...@@ -159,9 +188,11 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC ...@@ -159,9 +188,11 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
private final float fallbackMaxPlaybackSpeed; private final float fallbackMaxPlaybackSpeed;
private final long minUpdateIntervalMs; private final long minUpdateIntervalMs;
private final float proportionalControlFactor; private final float proportionalControlFactor;
private final long targetLiveOffsetRebufferDeltaUs;
private long mediaConfigurationTargetLiveOffsetUs; private long mediaConfigurationTargetLiveOffsetUs;
private long targetLiveOffsetOverrideUs; private long targetLiveOffsetOverrideUs;
private long idealTargetLiveOffsetUs;
private long minTargetLiveOffsetUs; private long minTargetLiveOffsetUs;
private long maxTargetLiveOffsetUs; private long maxTargetLiveOffsetUs;
private long currentTargetLiveOffsetUs; private long currentTargetLiveOffsetUs;
...@@ -175,11 +206,13 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC ...@@ -175,11 +206,13 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
float fallbackMinPlaybackSpeed, float fallbackMinPlaybackSpeed,
float fallbackMaxPlaybackSpeed, float fallbackMaxPlaybackSpeed,
long minUpdateIntervalMs, long minUpdateIntervalMs,
float proportionalControlFactor) { float proportionalControlFactor,
long targetLiveOffsetRebufferDeltaUs) {
this.fallbackMinPlaybackSpeed = fallbackMinPlaybackSpeed; this.fallbackMinPlaybackSpeed = fallbackMinPlaybackSpeed;
this.fallbackMaxPlaybackSpeed = fallbackMaxPlaybackSpeed; this.fallbackMaxPlaybackSpeed = fallbackMaxPlaybackSpeed;
this.minUpdateIntervalMs = minUpdateIntervalMs; this.minUpdateIntervalMs = minUpdateIntervalMs;
this.proportionalControlFactor = proportionalControlFactor; this.proportionalControlFactor = proportionalControlFactor;
this.targetLiveOffsetRebufferDeltaUs = targetLiveOffsetRebufferDeltaUs;
mediaConfigurationTargetLiveOffsetUs = C.TIME_UNSET; mediaConfigurationTargetLiveOffsetUs = C.TIME_UNSET;
targetLiveOffsetOverrideUs = C.TIME_UNSET; targetLiveOffsetOverrideUs = C.TIME_UNSET;
minTargetLiveOffsetUs = C.TIME_UNSET; minTargetLiveOffsetUs = C.TIME_UNSET;
...@@ -188,6 +221,7 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC ...@@ -188,6 +221,7 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
maxPlaybackSpeed = fallbackMaxPlaybackSpeed; maxPlaybackSpeed = fallbackMaxPlaybackSpeed;
adjustedPlaybackSpeed = 1.0f; adjustedPlaybackSpeed = 1.0f;
lastPlaybackSpeedUpdateMs = C.TIME_UNSET; lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
idealTargetLiveOffsetUs = C.TIME_UNSET;
currentTargetLiveOffsetUs = C.TIME_UNSET; currentTargetLiveOffsetUs = C.TIME_UNSET;
} }
...@@ -214,6 +248,19 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC ...@@ -214,6 +248,19 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
} }
@Override @Override
public void notifyRebuffer() {
if (currentTargetLiveOffsetUs == C.TIME_UNSET) {
return;
}
currentTargetLiveOffsetUs += targetLiveOffsetRebufferDeltaUs;
if (maxTargetLiveOffsetUs != C.TIME_UNSET
&& currentTargetLiveOffsetUs > maxTargetLiveOffsetUs) {
currentTargetLiveOffsetUs = maxTargetLiveOffsetUs;
}
lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
}
@Override
public float getAdjustedPlaybackSpeed(long liveOffsetUs) { public float getAdjustedPlaybackSpeed(long liveOffsetUs) {
if (mediaConfigurationTargetLiveOffsetUs == C.TIME_UNSET) { if (mediaConfigurationTargetLiveOffsetUs == C.TIME_UNSET) {
return 1f; return 1f;
...@@ -254,9 +301,10 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC ...@@ -254,9 +301,10 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
idealOffsetUs = maxTargetLiveOffsetUs; idealOffsetUs = maxTargetLiveOffsetUs;
} }
} }
if (currentTargetLiveOffsetUs == idealOffsetUs) { if (idealTargetLiveOffsetUs == idealOffsetUs) {
return; return;
} }
idealTargetLiveOffsetUs = idealOffsetUs;
currentTargetLiveOffsetUs = idealOffsetUs; currentTargetLiveOffsetUs = idealOffsetUs;
lastPlaybackSpeedUpdateMs = C.TIME_UNSET; lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
} }
......
...@@ -189,7 +189,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -189,7 +189,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private boolean released; private boolean released;
private boolean pauseAtEndOfWindow; private boolean pauseAtEndOfWindow;
private boolean pendingPauseAtEndOfPeriod; private boolean pendingPauseAtEndOfPeriod;
private boolean rebuffering; private boolean isRebuffering;
private boolean shouldContinueLoading; private boolean shouldContinueLoading;
@Player.RepeatMode private int repeatMode; @Player.RepeatMode private int repeatMode;
private boolean shuffleModeEnabled; private boolean shuffleModeEnabled;
...@@ -733,7 +733,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -733,7 +733,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
playbackInfoUpdate.incrementPendingOperationAcks(operationAck ? 1 : 0); playbackInfoUpdate.incrementPendingOperationAcks(operationAck ? 1 : 0);
playbackInfoUpdate.setPlayWhenReadyChangeReason(reason); playbackInfoUpdate.setPlayWhenReadyChangeReason(reason);
playbackInfo = playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason); playbackInfo = playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason);
rebuffering = false; isRebuffering = false;
if (!shouldPlayWhenReady()) { if (!shouldPlayWhenReady()) {
stopRenderers(); stopRenderers();
updatePlaybackPositions(); updatePlaybackPositions();
...@@ -811,7 +811,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -811,7 +811,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
private void startRenderers() throws ExoPlaybackException { private void startRenderers() throws ExoPlaybackException {
rebuffering = false; isRebuffering = false;
mediaClock.start(); mediaClock.start();
for (Renderer renderer : renderers) { for (Renderer renderer : renderers) {
if (isRendererEnabled(renderer)) { if (isRendererEnabled(renderer)) {
...@@ -868,6 +868,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -868,6 +868,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
// Adjust live playback speed to new position. // Adjust live playback speed to new position.
if (playbackInfo.playWhenReady if (playbackInfo.playWhenReady
&& playbackInfo.playbackState == Player.STATE_READY
&& isCurrentPeriodInMovingLiveWindow() && isCurrentPeriodInMovingLiveWindow()
&& playbackInfo.playbackParameters.speed == 1f) { && playbackInfo.playbackParameters.speed == 1f) {
float adjustedSpeed = float adjustedSpeed =
...@@ -960,8 +961,9 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -960,8 +961,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
} else if (playbackInfo.playbackState == Player.STATE_READY } else if (playbackInfo.playbackState == Player.STATE_READY
&& !(enabledRendererCount == 0 ? isTimelineReady() : renderersAllowPlayback)) { && !(enabledRendererCount == 0 ? isTimelineReady() : renderersAllowPlayback)) {
rebuffering = shouldPlayWhenReady(); isRebuffering = shouldPlayWhenReady();
setState(Player.STATE_BUFFERING); setState(Player.STATE_BUFFERING);
livePlaybackSpeedControl.notifyRebuffer();
stopRenderers(); stopRenderers();
} }
...@@ -1168,7 +1170,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -1168,7 +1170,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
boolean forceBufferingState) boolean forceBufferingState)
throws ExoPlaybackException { throws ExoPlaybackException {
stopRenderers(); stopRenderers();
rebuffering = false; isRebuffering = false;
if (forceBufferingState || playbackInfo.playbackState == Player.STATE_READY) { if (forceBufferingState || playbackInfo.playbackState == Player.STATE_READY) {
setState(Player.STATE_BUFFERING); setState(Player.STATE_BUFFERING);
} }
...@@ -1311,7 +1313,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -1311,7 +1313,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
boolean releaseMediaSourceList, boolean releaseMediaSourceList,
boolean resetError) { boolean resetError) {
handler.removeMessages(MSG_DO_SOME_WORK); handler.removeMessages(MSG_DO_SOME_WORK);
rebuffering = false; isRebuffering = false;
mediaClock.stop(); mediaClock.stop();
rendererPositionUs = 0; rendererPositionUs = 0;
for (Renderer renderer : renderers) { for (Renderer renderer : renderers) {
...@@ -1701,7 +1703,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -1701,7 +1703,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|| loadControl.shouldStartPlayback( || loadControl.shouldStartPlayback(
getTotalBufferedDurationUs(), getTotalBufferedDurationUs(),
mediaClock.getPlaybackParameters().speed, mediaClock.getPlaybackParameters().speed,
rebuffering, isRebuffering,
targetLiveOffsetUs); targetLiveOffsetUs);
} }
......
...@@ -41,6 +41,14 @@ public interface LivePlaybackSpeedControl { ...@@ -41,6 +41,14 @@ public interface LivePlaybackSpeedControl {
void setTargetLiveOffsetOverrideUs(long liveOffsetUs); void setTargetLiveOffsetOverrideUs(long liveOffsetUs);
/** /**
* Notifies the live playback speed control that a rebuffer occurred.
*
* <p>A rebuffer is defined to be caused by buffer depletion rather than a user action. Hence this
* method is not called during initial or when buffering as a result of a seek operation.
*/
void notifyRebuffer();
/**
* Returns the adjusted playback speed in order get closer towards the {@link * Returns the adjusted playback speed in order get closer towards the {@link
* #getTargetLiveOffsetUs() target live offset}. * #getTargetLiveOffsetUs() target live offset}.
* *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment