diff --git a/library/core/src/main/java/com/google/android/exoplayer2/DefaultLivePlaybackSpeedControl.java b/library/core/src/main/java/com/google/android/exoplayer2/DefaultLivePlaybackSpeedControl.java index 1107d90aa4..63de8cf581 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/DefaultLivePlaybackSpeedControl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/DefaultLivePlaybackSpeedControl.java @@ -122,7 +122,7 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC * @return This builder, for convenience. */ public Builder setMinUpdateIntervalMs(long minUpdateIntervalMs) { - Assertions.checkArgument(minUpdateIntervalMs >= 0); + Assertions.checkArgument(minUpdateIntervalMs > 0); this.minUpdateIntervalMs = minUpdateIntervalMs; return this; } @@ -160,8 +160,14 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC private final long minUpdateIntervalMs; private final float proportionalControlFactor; - private LiveConfiguration mediaConfiguration; + private long mediaConfigurationTargetLiveOffsetUs; private long targetLiveOffsetOverrideUs; + private long minTargetLiveOffsetUs; + private long maxTargetLiveOffsetUs; + private long currentTargetLiveOffsetUs; + + private float maxPlaybackSpeed; + private float minPlaybackSpeed; private float adjustedPlaybackSpeed; private long lastPlaybackSpeedUpdateMs; @@ -174,28 +180,42 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC this.fallbackMaxPlaybackSpeed = fallbackMaxPlaybackSpeed; this.minUpdateIntervalMs = minUpdateIntervalMs; this.proportionalControlFactor = proportionalControlFactor; - mediaConfiguration = LiveConfiguration.UNSET; + mediaConfigurationTargetLiveOffsetUs = C.TIME_UNSET; targetLiveOffsetOverrideUs = C.TIME_UNSET; + minTargetLiveOffsetUs = C.TIME_UNSET; + maxTargetLiveOffsetUs = C.TIME_UNSET; + minPlaybackSpeed = fallbackMinPlaybackSpeed; + maxPlaybackSpeed = fallbackMaxPlaybackSpeed; adjustedPlaybackSpeed = 1.0f; lastPlaybackSpeedUpdateMs = C.TIME_UNSET; + currentTargetLiveOffsetUs = C.TIME_UNSET; } @Override public void setLiveConfiguration(LiveConfiguration liveConfiguration) { - this.mediaConfiguration = liveConfiguration; - lastPlaybackSpeedUpdateMs = C.TIME_UNSET; + mediaConfigurationTargetLiveOffsetUs = C.msToUs(liveConfiguration.targetLiveOffsetMs); + minTargetLiveOffsetUs = C.msToUs(liveConfiguration.minLiveOffsetMs); + maxTargetLiveOffsetUs = C.msToUs(liveConfiguration.maxLiveOffsetMs); + minPlaybackSpeed = + liveConfiguration.minPlaybackSpeed != C.RATE_UNSET + ? liveConfiguration.minPlaybackSpeed + : fallbackMinPlaybackSpeed; + maxPlaybackSpeed = + liveConfiguration.maxPlaybackSpeed != C.RATE_UNSET + ? liveConfiguration.maxPlaybackSpeed + : fallbackMaxPlaybackSpeed; + maybeResetTargetLiveOffsetUs(); } @Override public void setTargetLiveOffsetOverrideUs(long liveOffsetUs) { - this.targetLiveOffsetOverrideUs = liveOffsetUs; - lastPlaybackSpeedUpdateMs = C.TIME_UNSET; + targetLiveOffsetOverrideUs = liveOffsetUs; + maybeResetTargetLiveOffsetUs(); } @Override public float getAdjustedPlaybackSpeed(long liveOffsetUs) { - long targetLiveOffsetUs = getTargetLiveOffsetUs(); - if (targetLiveOffsetUs == C.TIME_UNSET) { + if (mediaConfigurationTargetLiveOffsetUs == C.TIME_UNSET) { return 1f; } if (lastPlaybackSpeedUpdateMs != C.TIME_UNSET @@ -204,34 +224,40 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC } lastPlaybackSpeedUpdateMs = SystemClock.elapsedRealtime(); - long liveOffsetErrorUs = liveOffsetUs - targetLiveOffsetUs; + long liveOffsetErrorUs = liveOffsetUs - currentTargetLiveOffsetUs; if (Math.abs(liveOffsetErrorUs) < MAXIMUM_LIVE_OFFSET_ERROR_US_FOR_UNIT_SPEED) { adjustedPlaybackSpeed = 1f; } else { float calculatedSpeed = 1f + proportionalControlFactor * liveOffsetErrorUs; adjustedPlaybackSpeed = - Util.constrainValue(calculatedSpeed, getMinPlaybackSpeed(), getMaxPlaybackSpeed()); + Util.constrainValue(calculatedSpeed, minPlaybackSpeed, maxPlaybackSpeed); } return adjustedPlaybackSpeed; } @Override public long getTargetLiveOffsetUs() { - return targetLiveOffsetOverrideUs != C.TIME_UNSET - && mediaConfiguration.targetLiveOffsetMs != C.TIME_UNSET - ? targetLiveOffsetOverrideUs - : C.msToUs(mediaConfiguration.targetLiveOffsetMs); + return currentTargetLiveOffsetUs; } - private float getMinPlaybackSpeed() { - return mediaConfiguration.minPlaybackSpeed != C.RATE_UNSET - ? mediaConfiguration.minPlaybackSpeed - : fallbackMinPlaybackSpeed; - } - - private float getMaxPlaybackSpeed() { - return mediaConfiguration.maxPlaybackSpeed != C.RATE_UNSET - ? mediaConfiguration.maxPlaybackSpeed - : fallbackMaxPlaybackSpeed; + private void maybeResetTargetLiveOffsetUs() { + long idealOffsetUs = C.TIME_UNSET; + if (mediaConfigurationTargetLiveOffsetUs != C.TIME_UNSET) { + idealOffsetUs = + targetLiveOffsetOverrideUs != C.TIME_UNSET + ? targetLiveOffsetOverrideUs + : mediaConfigurationTargetLiveOffsetUs; + if (minTargetLiveOffsetUs != C.TIME_UNSET && idealOffsetUs < minTargetLiveOffsetUs) { + idealOffsetUs = minTargetLiveOffsetUs; + } + if (maxTargetLiveOffsetUs != C.TIME_UNSET && idealOffsetUs > maxTargetLiveOffsetUs) { + idealOffsetUs = maxTargetLiveOffsetUs; + } + } + if (currentTargetLiveOffsetUs == idealOffsetUs) { + return; + } + currentTargetLiveOffsetUs = idealOffsetUs; + lastPlaybackSpeedUpdateMs = C.TIME_UNSET; } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/DefaultLivePlaybackSpeedControlTest.java b/library/core/src/test/java/com/google/android/exoplayer2/DefaultLivePlaybackSpeedControlTest.java index 50fcf96283..8ec49ebabc 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/DefaultLivePlaybackSpeedControlTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/DefaultLivePlaybackSpeedControlTest.java @@ -37,13 +37,13 @@ public class DefaultLivePlaybackSpeedControlTest { } @Test - public void getTargetLiveOffsetUs_afterUpdateLiveConfiguration_usesMediaLiveOffset() { + public void getTargetLiveOffsetUs_afterSetLiveConfiguration_usesMediaLiveOffset() { DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl = new DefaultLivePlaybackSpeedControl.Builder().build(); defaultLivePlaybackSpeedControl.setLiveConfiguration( new LiveConfiguration( /* targetLiveOffsetMs= */ 42, - /* minLiveOffsetMs= */ 200, + /* minLiveOffsetMs= */ 5, /* maxLiveOffsetMs= */ 400, /* minPlaybackSpeed= */ 1f, /* maxPlaybackSpeed= */ 1f)); @@ -52,7 +52,59 @@ public class DefaultLivePlaybackSpeedControlTest { } @Test - public void getTargetLiveOffsetUs_withOverrideTargetLiveOffsetUs_usesOverride() { + public void + getTargetLiveOffsetUs_afterSetLiveConfigurationWithTargetGreaterThanMax_usesMaxLiveOffset() { + DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl = + new DefaultLivePlaybackSpeedControl.Builder().build(); + defaultLivePlaybackSpeedControl.setLiveConfiguration( + new LiveConfiguration( + /* targetLiveOffsetMs= */ 4321, + /* minLiveOffsetMs= */ 5, + /* maxLiveOffsetMs= */ 400, + /* minPlaybackSpeed= */ 1f, + /* maxPlaybackSpeed= */ 1f)); + + assertThat(defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs()).isEqualTo(400_000); + } + + @Test + public void + getTargetLiveOffsetUs_afterSetLiveConfigurationWithTargetLessThanMin_usesMinLiveOffset() { + DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl = + new DefaultLivePlaybackSpeedControl.Builder().build(); + defaultLivePlaybackSpeedControl.setLiveConfiguration( + new LiveConfiguration( + /* targetLiveOffsetMs= */ 3, + /* minLiveOffsetMs= */ 5, + /* maxLiveOffsetMs= */ 400, + /* minPlaybackSpeed= */ 1f, + /* maxPlaybackSpeed= */ 1f)); + + assertThat(defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs()).isEqualTo(5_000); + } + + @Test + public void getTargetLiveOffsetUs_withSetTargetLiveOffsetOverrideUs_usesOverride() { + DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl = + new DefaultLivePlaybackSpeedControl.Builder().build(); + + defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(321_000); + defaultLivePlaybackSpeedControl.setLiveConfiguration( + new LiveConfiguration( + /* targetLiveOffsetMs= */ 42, + /* minLiveOffsetMs= */ 5, + /* maxLiveOffsetMs= */ 400, + /* minPlaybackSpeed= */ 1f, + /* maxPlaybackSpeed= */ 1f)); + + long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs(); + + assertThat(targetLiveOffsetUs).isEqualTo(321_000); + } + + @Test + public void + getTargetLiveOffsetUs_withSetTargetLiveOffsetOverrideUsGreaterThanMax_usesMaxLiveOffset() { DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl = new DefaultLivePlaybackSpeedControl.Builder().build(); @@ -60,19 +112,39 @@ public class DefaultLivePlaybackSpeedControlTest { defaultLivePlaybackSpeedControl.setLiveConfiguration( new LiveConfiguration( /* targetLiveOffsetMs= */ 42, - /* minLiveOffsetMs= */ 200, + /* minLiveOffsetMs= */ 5, /* maxLiveOffsetMs= */ 400, /* minPlaybackSpeed= */ 1f, /* maxPlaybackSpeed= */ 1f)); long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs(); - assertThat(targetLiveOffsetUs).isEqualTo(123_456_789); + assertThat(targetLiveOffsetUs).isEqualTo(400_000); } @Test public void - getTargetLiveOffsetUs_afterOverrideTargetLiveOffset_withoutMediaConfiguration_returnsUnset() { + getTargetLiveOffsetUs_withSetTargetLiveOffsetOverrideUsLessThanMin_usesMinLiveOffset() { + DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl = + new DefaultLivePlaybackSpeedControl.Builder().build(); + + defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(3_141); + defaultLivePlaybackSpeedControl.setLiveConfiguration( + new LiveConfiguration( + /* targetLiveOffsetMs= */ 42, + /* minLiveOffsetMs= */ 5, + /* maxLiveOffsetMs= */ 400, + /* minPlaybackSpeed= */ 1f, + /* maxPlaybackSpeed= */ 1f)); + + long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs(); + + assertThat(targetLiveOffsetUs).isEqualTo(5_000); + } + + @Test + public void + getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideWithoutMediaConfiguration_returnsUnset() { DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl = new DefaultLivePlaybackSpeedControl.Builder().build(); defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(123_456_789); @@ -84,14 +156,14 @@ public class DefaultLivePlaybackSpeedControlTest { @Test public void - getTargetLiveOffsetUs_afterOverrideTargetLiveOffsetUsWithTimeUnset_usesMediaLiveOffset() { + getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideWithTimeUnset_usesMediaLiveOffset() { DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl = new DefaultLivePlaybackSpeedControl.Builder().build(); defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(123_456_789); defaultLivePlaybackSpeedControl.setLiveConfiguration( new LiveConfiguration( /* targetLiveOffsetMs= */ 42, - /* minLiveOffsetMs= */ 200, + /* minLiveOffsetMs= */ 5, /* maxLiveOffsetMs= */ 400, /* minPlaybackSpeed= */ 1f, /* maxPlaybackSpeed= */ 1f)); @@ -294,7 +366,8 @@ public class DefaultLivePlaybackSpeedControlTest { } @Test - public void adjustPlaybackSpeed_repeatedCallAfterUpdateLiveConfiguration_updatesSpeedAgain() { + public void + adjustPlaybackSpeed_repeatedCallAfterUpdateLiveConfigurationWithSameOffset_returnsSameAdjustedSpeed() { DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl = new DefaultLivePlaybackSpeedControl.Builder().setMinUpdateIntervalMs(123).build(); defaultLivePlaybackSpeedControl.setLiveConfiguration( @@ -317,6 +390,34 @@ public class DefaultLivePlaybackSpeedControlTest { float adjustedSpeed2 = defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_500_000); + assertThat(adjustedSpeed1).isEqualTo(adjustedSpeed2); + } + + @Test + public void + adjustPlaybackSpeed_repeatedCallAfterUpdateLiveConfigurationWithNewOffset_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.setLiveConfiguration( + new LiveConfiguration( + /* targetLiveOffsetMs= */ 1_000, + /* minLiveOffsetMs= */ C.TIME_UNSET, + /* maxLiveOffsetMs= */ C.TIME_UNSET, + /* minPlaybackSpeed= */ C.RATE_UNSET, + /* maxPlaybackSpeed= */ C.RATE_UNSET)); + float adjustedSpeed2 = + defaultLivePlaybackSpeedControl.getAdjustedPlaybackSpeed(/* liveOffsetUs= */ 2_500_000); + assertThat(adjustedSpeed1).isNotEqualTo(adjustedSpeed2); }