Increase target live offset when rebuffering.

Issue: #4904
PiperOrigin-RevId: 340654178
This commit is contained in:
tonihei 2020-11-04 16:33:05 +00:00 committed by Andrew Lewis
parent 2416d99857
commit effbc22a62
4 changed files with 250 additions and 20 deletions

View File

@ -33,6 +33,10 @@ import com.google.android.exoplayer2.util.Util;
* fallback values set with {@link Builder#setFallbackMinPlaybackSpeed(float)} and {@link
* Builder#setFallbackMaxPlaybackSpeed(float)} or the {@link #DEFAULT_FALLBACK_MIN_PLAYBACK_SPEED
* 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 {
@ -60,6 +64,12 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
*/
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
* unit speed (1.0f) is used.
@ -73,6 +83,7 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
private float fallbackMaxPlaybackSpeed;
private long minUpdateIntervalMs;
private float proportionalControlFactorUs;
private long targetLiveOffsetIncrementOnRebufferUs;
/** Creates a builder. */
public Builder() {
@ -80,6 +91,8 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
fallbackMaxPlaybackSpeed = DEFAULT_FALLBACK_MAX_PLAYBACK_SPEED;
minUpdateIntervalMs = DEFAULT_MIN_UPDATE_INTERVAL_MS;
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
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. */
public DefaultLivePlaybackSpeedControl build() {
return new DefaultLivePlaybackSpeedControl(
fallbackMinPlaybackSpeed,
fallbackMaxPlaybackSpeed,
minUpdateIntervalMs,
proportionalControlFactorUs);
proportionalControlFactorUs,
targetLiveOffsetIncrementOnRebufferUs);
}
}
@ -159,9 +188,11 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
private final float fallbackMaxPlaybackSpeed;
private final long minUpdateIntervalMs;
private final float proportionalControlFactor;
private final long targetLiveOffsetRebufferDeltaUs;
private long mediaConfigurationTargetLiveOffsetUs;
private long targetLiveOffsetOverrideUs;
private long idealTargetLiveOffsetUs;
private long minTargetLiveOffsetUs;
private long maxTargetLiveOffsetUs;
private long currentTargetLiveOffsetUs;
@ -175,11 +206,13 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
float fallbackMinPlaybackSpeed,
float fallbackMaxPlaybackSpeed,
long minUpdateIntervalMs,
float proportionalControlFactor) {
float proportionalControlFactor,
long targetLiveOffsetRebufferDeltaUs) {
this.fallbackMinPlaybackSpeed = fallbackMinPlaybackSpeed;
this.fallbackMaxPlaybackSpeed = fallbackMaxPlaybackSpeed;
this.minUpdateIntervalMs = minUpdateIntervalMs;
this.proportionalControlFactor = proportionalControlFactor;
this.targetLiveOffsetRebufferDeltaUs = targetLiveOffsetRebufferDeltaUs;
mediaConfigurationTargetLiveOffsetUs = C.TIME_UNSET;
targetLiveOffsetOverrideUs = C.TIME_UNSET;
minTargetLiveOffsetUs = C.TIME_UNSET;
@ -188,6 +221,7 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
maxPlaybackSpeed = fallbackMaxPlaybackSpeed;
adjustedPlaybackSpeed = 1.0f;
lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
idealTargetLiveOffsetUs = C.TIME_UNSET;
currentTargetLiveOffsetUs = C.TIME_UNSET;
}
@ -213,6 +247,19 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
maybeResetTargetLiveOffsetUs();
}
@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) {
if (mediaConfigurationTargetLiveOffsetUs == C.TIME_UNSET) {
@ -254,9 +301,10 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
idealOffsetUs = maxTargetLiveOffsetUs;
}
}
if (currentTargetLiveOffsetUs == idealOffsetUs) {
if (idealTargetLiveOffsetUs == idealOffsetUs) {
return;
}
idealTargetLiveOffsetUs = idealOffsetUs;
currentTargetLiveOffsetUs = idealOffsetUs;
lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
}

View File

@ -189,7 +189,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private boolean released;
private boolean pauseAtEndOfWindow;
private boolean pendingPauseAtEndOfPeriod;
private boolean rebuffering;
private boolean isRebuffering;
private boolean shouldContinueLoading;
@Player.RepeatMode private int repeatMode;
private boolean shuffleModeEnabled;
@ -733,7 +733,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
playbackInfoUpdate.incrementPendingOperationAcks(operationAck ? 1 : 0);
playbackInfoUpdate.setPlayWhenReadyChangeReason(reason);
playbackInfo = playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason);
rebuffering = false;
isRebuffering = false;
if (!shouldPlayWhenReady()) {
stopRenderers();
updatePlaybackPositions();
@ -811,7 +811,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
private void startRenderers() throws ExoPlaybackException {
rebuffering = false;
isRebuffering = false;
mediaClock.start();
for (Renderer renderer : renderers) {
if (isRendererEnabled(renderer)) {
@ -868,6 +868,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
// Adjust live playback speed to new position.
if (playbackInfo.playWhenReady
&& playbackInfo.playbackState == Player.STATE_READY
&& isCurrentPeriodInMovingLiveWindow()
&& playbackInfo.playbackParameters.speed == 1f) {
float adjustedSpeed =
@ -960,8 +961,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
} else if (playbackInfo.playbackState == Player.STATE_READY
&& !(enabledRendererCount == 0 ? isTimelineReady() : renderersAllowPlayback)) {
rebuffering = shouldPlayWhenReady();
isRebuffering = shouldPlayWhenReady();
setState(Player.STATE_BUFFERING);
livePlaybackSpeedControl.notifyRebuffer();
stopRenderers();
}
@ -1168,7 +1170,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
boolean forceBufferingState)
throws ExoPlaybackException {
stopRenderers();
rebuffering = false;
isRebuffering = false;
if (forceBufferingState || playbackInfo.playbackState == Player.STATE_READY) {
setState(Player.STATE_BUFFERING);
}
@ -1311,7 +1313,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
boolean releaseMediaSourceList,
boolean resetError) {
handler.removeMessages(MSG_DO_SOME_WORK);
rebuffering = false;
isRebuffering = false;
mediaClock.stop();
rendererPositionUs = 0;
for (Renderer renderer : renderers) {
@ -1701,7 +1703,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|| loadControl.shouldStartPlayback(
getTotalBufferedDurationUs(),
mediaClock.getPlaybackParameters().speed,
rebuffering,
isRebuffering,
targetLiveOffsetUs);
}

View File

@ -40,6 +40,14 @@ public interface LivePlaybackSpeedControl {
*/
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
* #getTargetLiveOffsetUs() target live offset}.

View File

@ -19,7 +19,10 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.MediaItem.LiveConfiguration;
import com.google.common.collect.Iterables;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.shadows.ShadowSystemClock;
@ -37,7 +40,7 @@ public class DefaultLivePlaybackSpeedControlTest {
}
@Test
public void getTargetLiveOffsetUs_afterSetLiveConfiguration_usesMediaLiveOffset() {
public void getTargetLiveOffsetUs_afterSetLiveConfiguration_returnsMediaLiveOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
@ -53,7 +56,7 @@ public class DefaultLivePlaybackSpeedControlTest {
@Test
public void
getTargetLiveOffsetUs_afterSetLiveConfigurationWithTargetGreaterThanMax_usesMaxLiveOffset() {
getTargetLiveOffsetUs_afterSetLiveConfigurationWithTargetGreaterThanMax_returnsMaxLiveOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
@ -69,7 +72,7 @@ public class DefaultLivePlaybackSpeedControlTest {
@Test
public void
getTargetLiveOffsetUs_afterSetLiveConfigurationWithTargetLessThanMin_usesMinLiveOffset() {
getTargetLiveOffsetUs_afterSetLiveConfigurationWithTargetLessThanMin_returnsMinLiveOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
@ -84,7 +87,7 @@ public class DefaultLivePlaybackSpeedControlTest {
}
@Test
public void getTargetLiveOffsetUs_withSetTargetLiveOffsetOverrideUs_usesOverride() {
public void getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideUs_returnsOverride() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().build();
@ -104,7 +107,7 @@ public class DefaultLivePlaybackSpeedControlTest {
@Test
public void
getTargetLiveOffsetUs_withSetTargetLiveOffsetOverrideUsGreaterThanMax_usesMaxLiveOffset() {
getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideUsGreaterThanMax_returnsMaxLiveOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().build();
@ -124,7 +127,7 @@ public class DefaultLivePlaybackSpeedControlTest {
@Test
public void
getTargetLiveOffsetUs_withSetTargetLiveOffsetOverrideUsLessThanMin_usesMinLiveOffset() {
getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideUsLessThanMin_returnsMinLiveOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().build();
@ -156,7 +159,7 @@ public class DefaultLivePlaybackSpeedControlTest {
@Test
public void
getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideWithTimeUnset_usesMediaLiveOffset() {
getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideWithTimeUnset_returnsMediaLiveOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().build();
defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(123_456_789);
@ -174,6 +177,153 @@ public class DefaultLivePlaybackSpeedControlTest {
assertThat(targetLiveOffsetUs).isEqualTo(42_000);
}
@Test
public void getTargetLiveOffsetUs_afterNotifyRebuffer_returnsIncreasedTargetOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder()
.setTargetLiveOffsetIncrementOnRebufferMs(3)
.build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42,
/* minLiveOffsetMs= */ 5,
/* maxLiveOffsetMs= */ 400,
/* minPlaybackSpeed= */ 1f,
/* maxPlaybackSpeed= */ 1f));
long targetLiveOffsetBeforeUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
defaultLivePlaybackSpeedControl.notifyRebuffer();
long targetLiveOffsetAfterUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetAfterUs).isGreaterThan(targetLiveOffsetBeforeUs);
assertThat(targetLiveOffsetAfterUs - targetLiveOffsetBeforeUs).isEqualTo(3_000);
}
@Test
public void getTargetLiveOffsetUs_afterRepeatedNotifyRebuffer_returnsMaxLiveOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder()
.setTargetLiveOffsetIncrementOnRebufferMs(3)
.build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42,
/* minLiveOffsetMs= */ 5,
/* maxLiveOffsetMs= */ 400,
/* minPlaybackSpeed= */ 1f,
/* maxPlaybackSpeed= */ 1f));
List<Long> targetOffsetsUs = new ArrayList<>();
for (int i = 0; i < 500; i++) {
targetOffsetsUs.add(defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs());
defaultLivePlaybackSpeedControl.notifyRebuffer();
}
assertThat(targetOffsetsUs).isInOrder();
assertThat(Iterables.getLast(targetOffsetsUs)).isEqualTo(400_000);
}
@Test
public void
getTargetLiveOffsetUs_afterNotifyRebufferWithIncrementOfZero_returnsOriginalTargetOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder()
.setTargetLiveOffsetIncrementOnRebufferMs(0)
.build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42,
/* minLiveOffsetMs= */ 5,
/* maxLiveOffsetMs= */ 400,
/* minPlaybackSpeed= */ 1f,
/* maxPlaybackSpeed= */ 1f));
defaultLivePlaybackSpeedControl.notifyRebuffer();
long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetUs).isEqualTo(42_000);
}
@Test
public void
getTargetLiveOffsetUs_afterNotifyRebufferAndSetTargetLiveOffsetOverrideUs_returnsOverride() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder()
.setTargetLiveOffsetIncrementOnRebufferMs(3)
.build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42,
/* minLiveOffsetMs= */ 5,
/* maxLiveOffsetMs= */ 400,
/* minPlaybackSpeed= */ 1f,
/* maxPlaybackSpeed= */ 1f));
defaultLivePlaybackSpeedControl.notifyRebuffer();
defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(321_000);
long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetUs).isEqualTo(321_000);
}
@Test
public void
getTargetLiveOffsetUs_afterNotifyRebufferAndSetLiveConfigurationWithSameOffset_returnsIncreasedTargetOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder()
.setTargetLiveOffsetIncrementOnRebufferMs(3)
.build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42,
/* minLiveOffsetMs= */ 5,
/* maxLiveOffsetMs= */ 400,
/* minPlaybackSpeed= */ 1f,
/* maxPlaybackSpeed= */ 1f));
long targetLiveOffsetBeforeUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
defaultLivePlaybackSpeedControl.notifyRebuffer();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42,
/* minLiveOffsetMs= */ 3,
/* maxLiveOffsetMs= */ 450,
/* minPlaybackSpeed= */ 0.9f,
/* maxPlaybackSpeed= */ 1.1f));
long targetLiveOffsetAfterUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetAfterUs).isGreaterThan(targetLiveOffsetBeforeUs);
assertThat(targetLiveOffsetAfterUs - targetLiveOffsetBeforeUs).isEqualTo(3_000);
}
@Test
public void
getTargetLiveOffsetUs_afterNotifyRebufferAndSetLiveConfigurationWithNewOffset_returnsNewOffset() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder()
.setTargetLiveOffsetIncrementOnRebufferMs(3)
.build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42,
/* minLiveOffsetMs= */ 5,
/* maxLiveOffsetMs= */ 400,
/* minPlaybackSpeed= */ 1f,
/* maxPlaybackSpeed= */ 1f));
defaultLivePlaybackSpeedControl.notifyRebuffer();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 39,
/* minLiveOffsetMs= */ 3,
/* maxLiveOffsetMs= */ 450,
/* minPlaybackSpeed= */ 0.9f,
/* maxPlaybackSpeed= */ 1.1f));
long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetUs).isEqualTo(39_000);
}
@Test
public void adjustPlaybackSpeed_liveOffsetMatchesTargetOffset_returnsUnitSpeed() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
@ -367,7 +517,7 @@ public class DefaultLivePlaybackSpeedControlTest {
@Test
public void
adjustPlaybackSpeed_repeatedCallAfterUpdateLiveConfigurationWithSameOffset_returnsSameAdjustedSpeed() {
adjustPlaybackSpeed_repeatedCallAfterSetLiveConfigurationWithSameOffset_returnsSameAdjustedSpeed() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().setMinUpdateIntervalMs(123).build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
@ -395,7 +545,7 @@ public class DefaultLivePlaybackSpeedControlTest {
@Test
public void
adjustPlaybackSpeed_repeatedCallAfterUpdateLiveConfigurationWithNewOffset_updatesSpeedAgain() {
adjustPlaybackSpeed_repeatedCallAfterSetLiveConfigurationWithNewOffset_updatesSpeedAgain() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().setMinUpdateIntervalMs(123).build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
@ -422,7 +572,8 @@ public class DefaultLivePlaybackSpeedControlTest {
}
@Test
public void adjustPlaybackSpeed_repeatedCallAfterNewTargetLiveOffset_updatesSpeedAgain() {
public void
adjustPlaybackSpeed_repeatedCallAfterSetTargetLiveOffsetOverrideUs_updatesSpeedAgain() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().setMinUpdateIntervalMs(123).build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
@ -441,4 +592,25 @@ public class DefaultLivePlaybackSpeedControlTest {
assertThat(adjustedSpeed1).isNotEqualTo(adjustedSpeed2);
}
@Test
public void adjustPlaybackSpeed_repeatedCallAfterNotifyRebuffer_updatesSpeedAgain() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().setMinUpdateIntervalMs(123).build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
/* minLiveOffsetMs= */ C.TIME_UNSET,
/* maxLiveOffsetMs= */ C.TIME_UNSET,
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed1 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
defaultLivePlaybackSpeedControl.notifyRebuffer();
float adjustedSpeed2 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
assertThat(adjustedSpeed1).isNotEqualTo(adjustedSpeed2);
}
}