Decrease target live offset if safely possible.

To check what is safely possible we keep track of the live offset
corresponding to the buffered duration and only deecrease the
target offset to a safe margin from the buffered duration.

Also, while still possible (i.e. while the actual offset is larger
than the safe margin), we increase the target offset to the safe
margin to avoid rebuffers to start with.

Issue: #4904
PiperOrigin-RevId: 341396492
This commit is contained in:
tonihei 2020-11-09 14:53:50 +00:00 committed by kim-vde
parent b03df4e8b5
commit 86ae7ebac4
4 changed files with 321 additions and 26 deletions

View File

@ -15,6 +15,10 @@
*/
package com.google.android.exoplayer2;
import static com.google.common.primitives.Longs.max;
import static java.lang.Math.abs;
import static java.lang.Math.max;
import android.os.SystemClock;
import com.google.android.exoplayer2.MediaItem.LiveConfiguration;
import com.google.android.exoplayer2.util.Assertions;
@ -36,7 +40,10 @@ import com.google.android.exoplayer2.util.Util;
*
* <p>When the player rebuffers, the target live offset {@link
* Builder#setTargetLiveOffsetIncrementOnRebufferMs(long) is increased} to adjust to the reduced
* network capabilities.
* network capabilities. The live playback speed control also {@link
* Builder#setMinPossibleLiveOffsetSmoothingFactor(float) keeps track} of the minimum possible live
* offset to decrease the target live offset again if conditions improve. The minimum possible live
* offset is derived from the current offset and the duration of buffered media.
*/
public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedControl {
@ -70,6 +77,12 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
*/
public static final long DEFAULT_TARGET_LIVE_OFFSET_INCREMENT_ON_REBUFFER_MS = 500;
/**
* The default smoothing factor when smoothing the minimum possible live offset that can be
* achieved during playback.
*/
public static final float DEFAULT_MIN_POSSIBLE_LIVE_OFFSET_SMOOTHING_FACTOR = 0.999f;
/**
* The maximum difference between the current live offset and the target live offset for which
* unit speed (1.0f) is used.
@ -84,6 +97,7 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
private long minUpdateIntervalMs;
private float proportionalControlFactorUs;
private long targetLiveOffsetIncrementOnRebufferUs;
private float minPossibleLiveOffsetSmoothingFactor;
/** Creates a builder. */
public Builder() {
@ -93,6 +107,7 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
proportionalControlFactorUs = DEFAULT_PROPORTIONAL_CONTROL_FACTOR / C.MICROS_PER_SECOND;
targetLiveOffsetIncrementOnRebufferUs =
C.msToUs(DEFAULT_TARGET_LIVE_OFFSET_INCREMENT_ON_REBUFFER_MS);
minPossibleLiveOffsetSmoothingFactor = DEFAULT_MIN_POSSIBLE_LIVE_OFFSET_SMOOTHING_FACTOR;
}
/**
@ -173,6 +188,28 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
return this;
}
/**
* Sets the smoothing factor when smoothing the minimum possible live offset that can be
* achieved during playback.
*
* <p>The live playback speed control keeps track of the minimum possible live offset achievable
* during playback to know whether it can reduce the current target live offset. The minimum
* possible live offset is defined as {@code currentLiveOffset - bufferedDuration}. As the
* minimum possible live offset is constantly changing, it is smoothed over recent samples by
* applying exponential smoothing: {@code smoothedMinPossibleOffset = smoothingFactor x
* smoothedMinPossibleOffset + (1-smoothingFactor) x currentMinPossibleOffset}.
*
* @param minPossibleLiveOffsetSmoothingFactor The smoothing factor. Must be &ge; 0 and &lt; 1.
* @return This builder, for convenience.
*/
public Builder setMinPossibleLiveOffsetSmoothingFactor(
float minPossibleLiveOffsetSmoothingFactor) {
Assertions.checkArgument(
minPossibleLiveOffsetSmoothingFactor >= 0 && minPossibleLiveOffsetSmoothingFactor < 1f);
this.minPossibleLiveOffsetSmoothingFactor = minPossibleLiveOffsetSmoothingFactor;
return this;
}
/** Builds an instance. */
public DefaultLivePlaybackSpeedControl build() {
return new DefaultLivePlaybackSpeedControl(
@ -180,7 +217,8 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
fallbackMaxPlaybackSpeed,
minUpdateIntervalMs,
proportionalControlFactorUs,
targetLiveOffsetIncrementOnRebufferUs);
targetLiveOffsetIncrementOnRebufferUs,
minPossibleLiveOffsetSmoothingFactor);
}
}
@ -189,6 +227,7 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
private final long minUpdateIntervalMs;
private final float proportionalControlFactor;
private final long targetLiveOffsetRebufferDeltaUs;
private final float minPossibleLiveOffsetSmoothingFactor;
private long mediaConfigurationTargetLiveOffsetUs;
private long targetLiveOffsetOverrideUs;
@ -202,17 +241,22 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
private float adjustedPlaybackSpeed;
private long lastPlaybackSpeedUpdateMs;
private long smoothedMinPossibleLiveOffsetUs;
private long smoothedMinPossibleLiveOffsetDeviationUs;
private DefaultLivePlaybackSpeedControl(
float fallbackMinPlaybackSpeed,
float fallbackMaxPlaybackSpeed,
long minUpdateIntervalMs,
float proportionalControlFactor,
long targetLiveOffsetRebufferDeltaUs) {
long targetLiveOffsetRebufferDeltaUs,
float minPossibleLiveOffsetSmoothingFactor) {
this.fallbackMinPlaybackSpeed = fallbackMinPlaybackSpeed;
this.fallbackMaxPlaybackSpeed = fallbackMaxPlaybackSpeed;
this.minUpdateIntervalMs = minUpdateIntervalMs;
this.proportionalControlFactor = proportionalControlFactor;
this.targetLiveOffsetRebufferDeltaUs = targetLiveOffsetRebufferDeltaUs;
this.minPossibleLiveOffsetSmoothingFactor = minPossibleLiveOffsetSmoothingFactor;
mediaConfigurationTargetLiveOffsetUs = C.TIME_UNSET;
targetLiveOffsetOverrideUs = C.TIME_UNSET;
minTargetLiveOffsetUs = C.TIME_UNSET;
@ -223,6 +267,8 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
idealTargetLiveOffsetUs = C.TIME_UNSET;
currentTargetLiveOffsetUs = C.TIME_UNSET;
smoothedMinPossibleLiveOffsetUs = C.TIME_UNSET;
smoothedMinPossibleLiveOffsetDeviationUs = C.TIME_UNSET;
}
@Override
@ -261,16 +307,20 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
}
@Override
public float getAdjustedPlaybackSpeed(long liveOffsetUs) {
public float getAdjustedPlaybackSpeed(long liveOffsetUs, long bufferedDurationUs) {
if (mediaConfigurationTargetLiveOffsetUs == C.TIME_UNSET) {
return 1f;
}
updateSmoothedMinPossibleLiveOffsetUs(liveOffsetUs, bufferedDurationUs);
if (lastPlaybackSpeedUpdateMs != C.TIME_UNSET
&& SystemClock.elapsedRealtime() - lastPlaybackSpeedUpdateMs < minUpdateIntervalMs) {
return adjustedPlaybackSpeed;
}
lastPlaybackSpeedUpdateMs = SystemClock.elapsedRealtime();
adjustTargetLiveOffsetUs(liveOffsetUs);
long liveOffsetErrorUs = liveOffsetUs - currentTargetLiveOffsetUs;
if (Math.abs(liveOffsetErrorUs) < MAXIMUM_LIVE_OFFSET_ERROR_US_FOR_UNIT_SPEED) {
adjustedPlaybackSpeed = 1f;
@ -306,6 +356,67 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
}
idealTargetLiveOffsetUs = idealOffsetUs;
currentTargetLiveOffsetUs = idealOffsetUs;
smoothedMinPossibleLiveOffsetUs = C.TIME_UNSET;
smoothedMinPossibleLiveOffsetDeviationUs = C.TIME_UNSET;
lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
}
private void updateSmoothedMinPossibleLiveOffsetUs(long liveOffsetUs, long bufferedDurationUs) {
long minPossibleLiveOffsetUs = liveOffsetUs - bufferedDurationUs;
if (smoothedMinPossibleLiveOffsetUs == C.TIME_UNSET) {
smoothedMinPossibleLiveOffsetUs = minPossibleLiveOffsetUs;
smoothedMinPossibleLiveOffsetDeviationUs = 0;
} else {
// Use the maximum here to ensure we keep track of the upper bound of what is safely possible,
// not the average.
smoothedMinPossibleLiveOffsetUs =
max(
minPossibleLiveOffsetUs,
smooth(
smoothedMinPossibleLiveOffsetUs,
minPossibleLiveOffsetUs,
minPossibleLiveOffsetSmoothingFactor));
long minPossibleLiveOffsetDeviationUs =
abs(minPossibleLiveOffsetUs - smoothedMinPossibleLiveOffsetUs);
smoothedMinPossibleLiveOffsetDeviationUs =
smooth(
smoothedMinPossibleLiveOffsetDeviationUs,
minPossibleLiveOffsetDeviationUs,
minPossibleLiveOffsetSmoothingFactor);
}
}
private void adjustTargetLiveOffsetUs(long liveOffsetUs) {
// Stay in a safe distance (3 standard deviations = >99%) to the minimum possible live offset.
long safeOffsetUs =
smoothedMinPossibleLiveOffsetUs + 3 * smoothedMinPossibleLiveOffsetDeviationUs;
if (currentTargetLiveOffsetUs > safeOffsetUs) {
// There is room for decreasing the target offset towards the ideal or safe offset (whichever
// is larger). We want to limit the decrease so that the playback speed delta we achieve is
// the same as the maximum delta when slowing down towards the target.
long minUpdateIntervalUs = C.msToUs(minUpdateIntervalMs);
long decrementToOffsetCurrentSpeedUs =
(long) ((adjustedPlaybackSpeed - 1f) * minUpdateIntervalUs);
long decrementToIncreaseSpeedUs = (long) ((maxPlaybackSpeed - 1f) * minUpdateIntervalUs);
long maxDecrementUs = decrementToOffsetCurrentSpeedUs + decrementToIncreaseSpeedUs;
currentTargetLiveOffsetUs =
max(safeOffsetUs, idealTargetLiveOffsetUs, currentTargetLiveOffsetUs - maxDecrementUs);
} else {
// We'd like to reach a stable condition where the current live offset stays just below the
// safe offset. But don't increase the target offset to more than what would allow us to slow
// down gradually from the current offset.
long offsetWhenSlowingDownNowUs =
liveOffsetUs - (long) (max(0f, adjustedPlaybackSpeed - 1f) / proportionalControlFactor);
currentTargetLiveOffsetUs =
Util.constrainValue(offsetWhenSlowingDownNowUs, currentTargetLiveOffsetUs, safeOffsetUs);
if (maxTargetLiveOffsetUs != C.TIME_UNSET
&& currentTargetLiveOffsetUs > maxTargetLiveOffsetUs) {
currentTargetLiveOffsetUs = maxTargetLiveOffsetUs;
}
}
}
private static long smooth(long smoothedValue, long newValue, float smoothingFactor) {
return (long) (smoothingFactor * smoothedValue + (1f - smoothingFactor) * newValue);
}
}

View File

@ -872,7 +872,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
&& isCurrentPeriodInMovingLiveWindow()
&& playbackInfo.playbackParameters.speed == 1f) {
float adjustedSpeed =
livePlaybackSpeedControl.getAdjustedPlaybackSpeed(getCurrentLiveOffsetUs());
livePlaybackSpeedControl.getAdjustedPlaybackSpeed(
getCurrentLiveOffsetUs(), getTotalBufferedDurationUs());
if (mediaClock.getPlaybackParameters().speed != adjustedSpeed) {
mediaClock.setPlaybackParameters(playbackInfo.playbackParameters.withSpeed(adjustedSpeed));
}

View File

@ -53,9 +53,10 @@ public interface LivePlaybackSpeedControl {
* #getTargetLiveOffsetUs() target live offset}.
*
* @param liveOffsetUs The current live offset, in microseconds.
* @param bufferedDurationUs The duration of media that's currently buffered, in microseconds.
* @return The adjusted playback speed.
*/
float getAdjustedPlaybackSpeed(long liveOffsetUs);
float getAdjustedPlaybackSpeed(long liveOffsetUs, long bufferedDurationUs);
/**
* Returns the current target live offset, in microseconds, or {@link C#TIME_UNSET} if no target

View File

@ -324,6 +324,172 @@ public class DefaultLivePlaybackSpeedControlTest {
assertThat(targetLiveOffsetUs).isEqualTo(39_000);
}
@Test
public void
getTargetLiveOffsetUs_afterNotifyRebufferAndAdjustPlaybackSpeedWithLargeBufferedDuration_returnsDecreasedOffsetToIdealTarget() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder()
.setTargetLiveOffsetIncrementOnRebufferMs(3_000)
.setMinUpdateIntervalMs(100)
.build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42_000,
/* minLiveOffsetMs= */ 5_000,
/* maxLiveOffsetMs= */ 400_000,
/* minPlaybackSpeed= */ 0.9f,
/* maxPlaybackSpeed= */ 1.1f));
defaultLivePlaybackSpeedControl.notifyRebuffer();
long targetLiveOffsetAfterRebufferUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 45_000_000, /* bufferedDurationUs= */ 9_000_000);
long targetLiveOffsetAfterOneAdjustmentUs =
defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
for (int i = 0; i < 500; i++) {
ShadowSystemClock.advanceBy(Duration.ofMillis(100));
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 45_000_000, /* bufferedDurationUs= */ 9_000_000);
}
long targetLiveOffsetAfterManyAdjustmentsUs =
defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetAfterOneAdjustmentUs).isLessThan(targetLiveOffsetAfterRebufferUs);
assertThat(targetLiveOffsetAfterManyAdjustmentsUs)
.isLessThan(targetLiveOffsetAfterOneAdjustmentUs);
assertThat(targetLiveOffsetAfterManyAdjustmentsUs).isEqualTo(42_000_000);
}
@Test
public void
getTargetLiveOffsetUs_afterNotifyRebufferAndAdjustPlaybackSpeedWithSmallBufferedDuration_returnsDecreasedOffsetToSafeTarget() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder()
.setTargetLiveOffsetIncrementOnRebufferMs(3_000)
.setMinUpdateIntervalMs(100)
.build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42_000,
/* minLiveOffsetMs= */ 5_000,
/* maxLiveOffsetMs= */ 400_000,
/* minPlaybackSpeed= */ 0.9f,
/* maxPlaybackSpeed= */ 1.1f));
defaultLivePlaybackSpeedControl.notifyRebuffer();
long targetLiveOffsetAfterRebufferUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 45_000_000, /* bufferedDurationUs= */ 1_000_000);
long targetLiveOffsetAfterOneAdjustmentUs =
defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
for (int i = 0; i < 500; i++) {
ShadowSystemClock.advanceBy(Duration.ofMillis(100));
long noiseUs = ((i % 10) - 5) * 1_000;
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 45_000_000, /* bufferedDurationUs= */ 1_000_000 + noiseUs);
}
long targetLiveOffsetAfterManyAdjustmentsUs =
defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetAfterOneAdjustmentUs).isLessThan(targetLiveOffsetAfterRebufferUs);
assertThat(targetLiveOffsetAfterManyAdjustmentsUs)
.isLessThan(targetLiveOffsetAfterOneAdjustmentUs);
// Should be at least be at the minimum buffered position.
assertThat(targetLiveOffsetAfterManyAdjustmentsUs).isGreaterThan(44_005_000);
}
@Test
public void
getTargetLiveOffsetUs_afterAdjustPlaybackSpeedWithLiveOffsetAroundCurrentTarget_returnsSafeTarget() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42_000,
/* minLiveOffsetMs= */ 5_000,
/* maxLiveOffsetMs= */ 400_000,
/* minPlaybackSpeed= */ 0.9f,
/* maxPlaybackSpeed= */ 1.1f));
long targetLiveOffsetBeforeUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
// Pretend to have a buffered duration at around the target duration with some artificial noise.
for (int i = 0; i < 500; i++) {
ShadowSystemClock.advanceBy(Duration.ofMillis(100));
long noiseUs = ((i % 10) - 5) * 1_000;
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 49_000_000, /* bufferedDurationUs= */ 7_000_000 + noiseUs);
}
ShadowSystemClock.advanceBy(Duration.ofMillis(100));
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 49_000_000, /* bufferedDurationUs= */ 7_000_000);
long targetLiveOffsetAfterUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetBeforeUs).isEqualTo(42_000_000);
assertThat(targetLiveOffsetAfterUs).isGreaterThan(42_005_000);
}
@Test
public void
getTargetLiveOffsetUs_afterAdjustPlaybackSpeedAndSmoothingFactorOfZero_ignoresSafeTargetAndReturnsCurrentTarget() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder()
.setMinPossibleLiveOffsetSmoothingFactor(0f)
.build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42_000,
/* minLiveOffsetMs= */ 5_000,
/* maxLiveOffsetMs= */ 400_000,
/* minPlaybackSpeed= */ 0.9f,
/* maxPlaybackSpeed= */ 1.1f));
long targetLiveOffsetBeforeUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
// Pretend to have a buffered duration at around the target duration with some artificial noise.
for (int i = 0; i < 500; i++) {
ShadowSystemClock.advanceBy(Duration.ofMillis(100));
long noiseUs = ((i % 10) - 5) * 1_000;
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 49_000_000, /* bufferedDurationUs= */ 7_000_000 + noiseUs);
}
ShadowSystemClock.advanceBy(Duration.ofMillis(100));
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 49_000_000, /* bufferedDurationUs= */ 7_000_000);
long targetLiveOffsetAfterUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetBeforeUs).isEqualTo(42_000_000);
// Despite the noise indicating it's unsafe here, we still return the target offset.
assertThat(targetLiveOffsetAfterUs).isEqualTo(42_000_000);
}
@Test
public void
getTargetLiveOffsetUs_afterAdjustPlaybackSpeedWithLiveOffsetLessThanCurrentTarget_returnsCurrentTarget() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder()
.setTargetLiveOffsetIncrementOnRebufferMs(3_000)
.setMinUpdateIntervalMs(100)
.build();
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 42_000,
/* minLiveOffsetMs= */ 5_000,
/* maxLiveOffsetMs= */ 400_000,
/* minPlaybackSpeed= */ 0.9f,
/* maxPlaybackSpeed= */ 1.1f));
long targetLiveOffsetBeforeUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 39_000_000, /* bufferedDurationUs= */ 1_000_000);
long targetLiveOffsetAfterUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetBeforeUs).isEqualTo(42_000_000);
assertThat(targetLiveOffsetAfterUs).isEqualTo(42_000_000);
}
@Test
public void adjustPlaybackSpeed_liveOffsetMatchesTargetOffset_returnsUnitSpeed() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
@ -337,7 +503,8 @@ public class DefaultLivePlaybackSpeedControlTest {
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_000_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 2_000_000, /* bufferedDurationUs= */ 1_000_000);
assertThat(adjustedSpeed).isEqualTo(1f);
}
@ -358,12 +525,14 @@ public class DefaultLivePlaybackSpeedControlTest {
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 2_000_000
- DefaultLivePlaybackSpeedControl.MAXIMUM_LIVE_OFFSET_ERROR_US_FOR_UNIT_SPEED
+ 1);
+ 1,
/* bufferedDurationUs= */ 1_000_000);
float adjustedSpeedJustBelowUpperErrorMargin =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 2_000_000
+ DefaultLivePlaybackSpeedControl.MAXIMUM_LIVE_OFFSET_ERROR_US_FOR_UNIT_SPEED
- 1);
- 1,
/* bufferedDurationUs= */ 1_000_000);
assertThat(adjustedSpeedJustAboveLowerErrorMargin).isEqualTo(1f);
assertThat(adjustedSpeedJustBelowUpperErrorMargin).isEqualTo(1f);
@ -382,7 +551,8 @@ public class DefaultLivePlaybackSpeedControlTest {
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 2_500_000, /* bufferedDurationUs= */ 1_000_000);
float expectedSpeedAccordingToDocumentation = 1f + 0.01f * (2.5f - 2f);
assertThat(adjustedSpeed).isEqualTo(expectedSpeedAccordingToDocumentation);
@ -403,7 +573,8 @@ public class DefaultLivePlaybackSpeedControlTest {
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 1_500_000, /* bufferedDurationUs= */ 1_000_000);
float expectedSpeedAccordingToDocumentation = 1f + 0.01f * (1.5f - 2f);
assertThat(adjustedSpeed).isEqualTo(expectedSpeedAccordingToDocumentation);
@ -425,7 +596,7 @@ public class DefaultLivePlaybackSpeedControlTest {
float adjustedSpeed =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 999_999_999_999L);
/* liveOffsetUs= */ 999_999_999_999L, /* bufferedDurationUs= */ 999_999_999_999L);
assertThat(adjustedSpeed).isEqualTo(1.5f);
}
@ -445,7 +616,7 @@ public class DefaultLivePlaybackSpeedControlTest {
float adjustedSpeed =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ -999_999_999_999L);
/* liveOffsetUs= */ -999_999_999_999L, /* bufferedDurationUs= */ 1_000_000);
assertThat(adjustedSpeed).isEqualTo(0.5f);
}
@ -465,7 +636,7 @@ public class DefaultLivePlaybackSpeedControlTest {
float adjustedSpeed =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 999_999_999_999L);
/* liveOffsetUs= */ 999_999_999_999L, /* bufferedDurationUs= */ 999_999_999_999L);
assertThat(adjustedSpeed).isEqualTo(2f);
}
@ -485,7 +656,7 @@ public class DefaultLivePlaybackSpeedControlTest {
float adjustedSpeed =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ -999_999_999_999L);
/* liveOffsetUs= */ -999_999_999_999L, /* bufferedDurationUs= */ 1_000_000);
assertThat(adjustedSpeed).isEqualTo(0.2f);
}
@ -503,13 +674,16 @@ public class DefaultLivePlaybackSpeedControlTest {
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed1 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 1_500_000, /* bufferedDurationUs= */ 1_000_000);
ShadowSystemClock.advanceBy(Duration.ofMillis(122));
float adjustedSpeed2 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 2_500_000, /* bufferedDurationUs= */ 1_000_000);
ShadowSystemClock.advanceBy(Duration.ofMillis(2));
float adjustedSpeed3 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 2_500_000, /* bufferedDurationUs= */ 1_000_000);
assertThat(adjustedSpeed1).isEqualTo(adjustedSpeed2);
assertThat(adjustedSpeed3).isNotEqualTo(adjustedSpeed2);
@ -529,7 +703,8 @@ public class DefaultLivePlaybackSpeedControlTest {
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed1 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 1_500_000, /* bufferedDurationUs= */ 1_000_000);
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 2_000,
@ -538,7 +713,8 @@ public class DefaultLivePlaybackSpeedControlTest {
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed2 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 2_500_000, /* bufferedDurationUs= */ 1_000_000);
assertThat(adjustedSpeed1).isEqualTo(adjustedSpeed2);
}
@ -557,7 +733,8 @@ public class DefaultLivePlaybackSpeedControlTest {
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed1 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 1_500_000, /* bufferedDurationUs= */ 1_000_000);
defaultLivePlaybackSpeedControl.setLiveConfiguration(
new LiveConfiguration(
/* targetLiveOffsetMs= */ 1_000,
@ -566,7 +743,8 @@ public class DefaultLivePlaybackSpeedControlTest {
/* minPlaybackSpeed= */ C.RATE_UNSET,
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed2 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 2_500_000, /* bufferedDurationUs= */ 1_000_000);
assertThat(adjustedSpeed1).isNotEqualTo(adjustedSpeed2);
}
@ -585,10 +763,12 @@ public class DefaultLivePlaybackSpeedControlTest {
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed1 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 1_500_000, /* bufferedDurationUs= */ 1_000_000);
defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(2_000_001);
float adjustedSpeed2 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 2_500_000, /* bufferedDurationUs= */ 1_000_000);
assertThat(adjustedSpeed1).isNotEqualTo(adjustedSpeed2);
}
@ -606,10 +786,12 @@ public class DefaultLivePlaybackSpeedControlTest {
/* maxPlaybackSpeed= */ C.RATE_UNSET));
float adjustedSpeed1 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 1_500_000, /* bufferedDurationUs= */ 1_000_000);
defaultLivePlaybackSpeedControl.notifyRebuffer();
float adjustedSpeed2 =
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(
/* liveOffsetUs= */ 2_500_000, /* bufferedDurationUs= */ 1_000_000);
assertThat(adjustedSpeed1).isNotEqualTo(adjustedSpeed2);
}