mirror of
https://github.com/androidx/media.git
synced 2025-05-14 02:59:52 +08:00
Add LivePlaybackSpeedControl interface and default implementation.
Issue: #4904 PiperOrigin-RevId: 336839908
This commit is contained in:
parent
13f1f3b3eb
commit
867d27fa72
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import com.google.android.exoplayer2.MediaItem.LiveConfiguration;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
/**
|
||||
* A {@link LivePlaybackSpeedControl} that adjusts the playback speed using a proportional
|
||||
* controller.
|
||||
*
|
||||
* <p>The control mechanism calculates the adjusted speed as {@code 1.0 + proportionalControlFactor
|
||||
* x (currentLiveOffsetSec - targetLiveOffsetSec)}. Unit speed (1.0f) is used, if the {@code
|
||||
* currentLiveOffsetSec} is marginally close to {@code targetLiveOffsetSec}, i.e. {@code
|
||||
* |currentLiveOffsetSec - targetLiveOffsetSec| <= MAXIMUM_LIVE_OFFSET_ERROR_US_FOR_UNIT_SPEED}.
|
||||
*
|
||||
* <p>The resulting speed is clamped to a minimum and maximum speed defined by the media, the
|
||||
* 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.
|
||||
*/
|
||||
public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedControl {
|
||||
|
||||
/**
|
||||
* The default minimum playback speed that should be used if no minimum playback speed is defined
|
||||
* by the media.
|
||||
*/
|
||||
public static final float DEFAULT_FALLBACK_MIN_PLAYBACK_SPEED = 0.97f;
|
||||
|
||||
/**
|
||||
* The default maximum playback speed that should be used if no maximum playback speed is defined
|
||||
* by the media.
|
||||
*/
|
||||
public static final float DEFAULT_FALLBACK_MAX_PLAYBACK_SPEED = 1.03f;
|
||||
|
||||
/**
|
||||
* The default {@link Builder#setMinUpdateIntervalMs(long) minimum interval} between playback
|
||||
* speed changes, in milliseconds.
|
||||
*/
|
||||
public static final long DEFAULT_MIN_UPDATE_INTERVAL_MS = 500;
|
||||
|
||||
/**
|
||||
* The default {@link Builder#setProportionalControlFactor(float) proportional control factor}
|
||||
* used to adjust the playback speed.
|
||||
*/
|
||||
public static final float DEFAULT_PROPORTIONAL_CONTROL_FACTOR = 0.05f;
|
||||
|
||||
/**
|
||||
* The maximum difference between the current live offset and the target live offset for which
|
||||
* unit speed (1.0f) is used.
|
||||
*/
|
||||
public static final long MAXIMUM_LIVE_OFFSET_ERROR_US_FOR_UNIT_SPEED = 5_000;
|
||||
|
||||
/** Builder for a {@link DefaultLivePlaybackSpeedControl}. */
|
||||
public static final class Builder {
|
||||
|
||||
private float fallbackMinPlaybackSpeed;
|
||||
private float fallbackMaxPlaybackSpeed;
|
||||
private long minUpdateIntervalMs;
|
||||
private float proportionalControlFactorUs;
|
||||
|
||||
/** Creates a builder. */
|
||||
public Builder() {
|
||||
fallbackMinPlaybackSpeed = DEFAULT_FALLBACK_MIN_PLAYBACK_SPEED;
|
||||
fallbackMaxPlaybackSpeed = DEFAULT_FALLBACK_MAX_PLAYBACK_SPEED;
|
||||
minUpdateIntervalMs = DEFAULT_MIN_UPDATE_INTERVAL_MS;
|
||||
proportionalControlFactorUs = DEFAULT_PROPORTIONAL_CONTROL_FACTOR / C.MICROS_PER_SECOND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum playback speed that should be used if no minimum playback speed is defined
|
||||
* by the media.
|
||||
*
|
||||
* <p>The default is {@link #DEFAULT_FALLBACK_MIN_PLAYBACK_SPEED}.
|
||||
*
|
||||
* @param fallbackMinPlaybackSpeed The fallback minimum playback speed.
|
||||
* @return This builder, for convenience.
|
||||
*/
|
||||
public Builder setFallbackMinPlaybackSpeed(float fallbackMinPlaybackSpeed) {
|
||||
Assertions.checkArgument(0 < fallbackMinPlaybackSpeed && fallbackMinPlaybackSpeed <= 1f);
|
||||
this.fallbackMinPlaybackSpeed = fallbackMinPlaybackSpeed;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum playback speed that should be used if no maximum playback speed is defined
|
||||
* by the media.
|
||||
*
|
||||
* <p>The default is {@link #DEFAULT_FALLBACK_MAX_PLAYBACK_SPEED}.
|
||||
*
|
||||
* @param fallbackMaxPlaybackSpeed The fallback maximum playback speed.
|
||||
* @return This builder, for convenience.
|
||||
*/
|
||||
public Builder setFallbackMaxPlaybackSpeed(float fallbackMaxPlaybackSpeed) {
|
||||
Assertions.checkArgument(fallbackMaxPlaybackSpeed >= 1f);
|
||||
this.fallbackMaxPlaybackSpeed = fallbackMaxPlaybackSpeed;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum interval between playback speed changes, in milliseconds.
|
||||
*
|
||||
* <p>The default is {@link #DEFAULT_MIN_UPDATE_INTERVAL_MS}.
|
||||
*
|
||||
* @param minUpdateIntervalMs The minimum interval between playback speed changes, in
|
||||
* milliseconds.
|
||||
* @return This builder, for convenience.
|
||||
*/
|
||||
public Builder setMinUpdateIntervalMs(long minUpdateIntervalMs) {
|
||||
Assertions.checkArgument(minUpdateIntervalMs >= 0);
|
||||
this.minUpdateIntervalMs = minUpdateIntervalMs;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the proportional control factor used to adjust the playback speed.
|
||||
*
|
||||
* <p>The adjusted playback speed is calculated as {@code 1.0 + proportionalControlFactor x
|
||||
* (currentLiveOffsetSec - targetLiveOffsetSec)}.
|
||||
*
|
||||
* <p>The default is {@link #DEFAULT_PROPORTIONAL_CONTROL_FACTOR}.
|
||||
*
|
||||
* @param proportionalControlFactor The proportional control factor used to adjust the playback
|
||||
* speed.
|
||||
* @return This builder, for convenience.
|
||||
*/
|
||||
public Builder setProportionalControlFactor(float proportionalControlFactor) {
|
||||
Assertions.checkArgument(proportionalControlFactor > 0);
|
||||
this.proportionalControlFactorUs = proportionalControlFactor / C.MICROS_PER_SECOND;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds an instance. */
|
||||
public DefaultLivePlaybackSpeedControl build() {
|
||||
return new DefaultLivePlaybackSpeedControl(
|
||||
fallbackMinPlaybackSpeed,
|
||||
fallbackMaxPlaybackSpeed,
|
||||
minUpdateIntervalMs,
|
||||
proportionalControlFactorUs);
|
||||
}
|
||||
}
|
||||
|
||||
private final float fallbackMinPlaybackSpeed;
|
||||
private final float fallbackMaxPlaybackSpeed;
|
||||
private final long minUpdateIntervalMs;
|
||||
private final float proportionalControlFactor;
|
||||
|
||||
private LiveConfiguration mediaConfiguration;
|
||||
private long targetLiveOffsetOverrideUs;
|
||||
private float adjustedPlaybackSpeed;
|
||||
private long lastPlaybackSpeedUpdateMs;
|
||||
|
||||
private DefaultLivePlaybackSpeedControl(
|
||||
float fallbackMinPlaybackSpeed,
|
||||
float fallbackMaxPlaybackSpeed,
|
||||
long minUpdateIntervalMs,
|
||||
float proportionalControlFactor) {
|
||||
this.fallbackMinPlaybackSpeed = fallbackMinPlaybackSpeed;
|
||||
this.fallbackMaxPlaybackSpeed = fallbackMaxPlaybackSpeed;
|
||||
this.minUpdateIntervalMs = minUpdateIntervalMs;
|
||||
this.proportionalControlFactor = proportionalControlFactor;
|
||||
mediaConfiguration = LiveConfiguration.UNSET;
|
||||
targetLiveOffsetOverrideUs = C.TIME_UNSET;
|
||||
adjustedPlaybackSpeed = 1.0f;
|
||||
lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLiveConfiguration(LiveConfiguration liveConfiguration) {
|
||||
this.mediaConfiguration = liveConfiguration;
|
||||
lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void overrideTargetLiveOffsetUs(long liveOffsetUs) {
|
||||
this.targetLiveOffsetOverrideUs = liveOffsetUs;
|
||||
lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float adjustPlaybackSpeed(long liveOffsetUs) {
|
||||
long targetLiveOffsetUs = getTargetLiveOffsetUs();
|
||||
if (targetLiveOffsetUs == C.TIME_UNSET) {
|
||||
return 1f;
|
||||
}
|
||||
if (lastPlaybackSpeedUpdateMs != C.TIME_UNSET
|
||||
&& SystemClock.elapsedRealtime() - lastPlaybackSpeedUpdateMs < minUpdateIntervalMs) {
|
||||
return adjustedPlaybackSpeed;
|
||||
}
|
||||
lastPlaybackSpeedUpdateMs = SystemClock.elapsedRealtime();
|
||||
|
||||
long liveOffsetErrorUs = liveOffsetUs - targetLiveOffsetUs;
|
||||
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());
|
||||
}
|
||||
return adjustedPlaybackSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTargetLiveOffsetUs() {
|
||||
return targetLiveOffsetOverrideUs != C.TIME_UNSET
|
||||
&& mediaConfiguration.targetLiveOffsetMs != C.TIME_UNSET
|
||||
? targetLiveOffsetOverrideUs
|
||||
: C.msToUs(mediaConfiguration.targetLiveOffsetMs);
|
||||
}
|
||||
|
||||
private float getMinPlaybackSpeed() {
|
||||
return mediaConfiguration.minPlaybackSpeed != C.RATE_UNSET
|
||||
? mediaConfiguration.minPlaybackSpeed
|
||||
: fallbackMinPlaybackSpeed;
|
||||
}
|
||||
|
||||
private float getMaxPlaybackSpeed() {
|
||||
return mediaConfiguration.maxPlaybackSpeed != C.RATE_UNSET
|
||||
? mediaConfiguration.maxPlaybackSpeed
|
||||
: fallbackMaxPlaybackSpeed;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import com.google.android.exoplayer2.MediaItem.LiveConfiguration;
|
||||
|
||||
/**
|
||||
* Controls the playback speed while playing live content in order to maintain a steady target live
|
||||
* offset.
|
||||
*/
|
||||
public interface LivePlaybackSpeedControl {
|
||||
|
||||
/**
|
||||
* Updates the live configuration defined by the media.
|
||||
*
|
||||
* @param liveConfiguration The {@link LiveConfiguration} as defined by the media.
|
||||
*/
|
||||
void updateLiveConfiguration(LiveConfiguration liveConfiguration);
|
||||
|
||||
/**
|
||||
* Overrides the {@link #updateLiveConfiguration configured} target live offset in microseconds,
|
||||
* or {@code C.TIME_UNSET} to delete a previous override.
|
||||
*
|
||||
* <p>If no target live offset is configured by {@link #updateLiveConfiguration}, this override
|
||||
* has no effect.
|
||||
*/
|
||||
void overrideTargetLiveOffsetUs(long liveOffsetUs);
|
||||
|
||||
/**
|
||||
* Returns the adjusted playback speed in order get closer towards the {@link
|
||||
* #getTargetLiveOffsetUs() target live offset}.
|
||||
*
|
||||
* @param liveOffsetUs The current live offset, in microseconds.
|
||||
* @return The adjusted playback speed.
|
||||
*/
|
||||
float adjustPlaybackSpeed(long liveOffsetUs);
|
||||
|
||||
/**
|
||||
* Returns the current target live offset, in microseconds, or {@link C#TIME_UNSET} if no target
|
||||
* live offset is defined for the current media.
|
||||
*/
|
||||
long getTargetLiveOffsetUs();
|
||||
}
|
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.MediaItem.LiveConfiguration;
|
||||
import java.time.Duration;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.shadows.ShadowSystemClock;
|
||||
|
||||
/** Unit test for {@link DefaultLivePlaybackSpeedControl}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DefaultLivePlaybackSpeedControlTest {
|
||||
|
||||
@Test
|
||||
public void getTargetLiveOffsetUs_returnsUnset() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().build();
|
||||
|
||||
assertThat(defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs()).isEqualTo(C.TIME_UNSET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTargetLiveOffsetUs_afterUpdateLiveConfiguration_usesMediaLiveOffset() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 42, /* minPlaybackSpeed= */ 1f, /* maxPlaybackSpeed= */ 1f));
|
||||
|
||||
assertThat(defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs()).isEqualTo(42_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTargetLiveOffsetUs_withOverrideTargetLiveOffsetUs_usesOverride() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().build();
|
||||
|
||||
defaultLivePlaybackSpeedControl.overrideTargetLiveOffsetUs(123_456_789);
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 42, /* minPlaybackSpeed= */ 1f, /* maxPlaybackSpeed= */ 1f));
|
||||
|
||||
long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
|
||||
|
||||
assertThat(targetLiveOffsetUs).isEqualTo(123_456_789);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
getTargetLiveOffsetUs_afterOverrideTargetLiveOffset_withoutMediaConfiguration_returnsUnset() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().build();
|
||||
defaultLivePlaybackSpeedControl.overrideTargetLiveOffsetUs(123_456_789);
|
||||
|
||||
long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
|
||||
|
||||
assertThat(targetLiveOffsetUs).isEqualTo(C.TIME_UNSET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
getTargetLiveOffsetUs_afterOverrideTargetLiveOffsetUsWithTimeUnset_usesMediaLiveOffset() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().build();
|
||||
defaultLivePlaybackSpeedControl.overrideTargetLiveOffsetUs(123_456_789);
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 42, /* minPlaybackSpeed= */ 1f, /* maxPlaybackSpeed= */ 1f));
|
||||
defaultLivePlaybackSpeedControl.overrideTargetLiveOffsetUs(C.TIME_UNSET);
|
||||
|
||||
long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
|
||||
|
||||
assertThat(targetLiveOffsetUs).isEqualTo(42_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustPlaybackSpeed_liveOffsetMatchesTargetOffset_returnsUnitSpeed() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
|
||||
float adjustedSpeed =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 2_000_000);
|
||||
|
||||
assertThat(adjustedSpeed).isEqualTo(1f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustPlaybackSpeed_liveOffsetWithinAcceptableErrorMargin_returnsUnitSpeed() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
|
||||
float adjustedSpeedJustAboveLowerErrorMargin =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(
|
||||
/* liveOffsetUs= */ 2_000_000
|
||||
- DefaultLivePlaybackSpeedControl.MAXIMUM_LIVE_OFFSET_ERROR_US_FOR_UNIT_SPEED
|
||||
+ 1);
|
||||
float adjustedSpeedJustBelowUpperErrorMargin =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(
|
||||
/* liveOffsetUs= */ 2_000_000
|
||||
+ DefaultLivePlaybackSpeedControl.MAXIMUM_LIVE_OFFSET_ERROR_US_FOR_UNIT_SPEED
|
||||
- 1);
|
||||
|
||||
assertThat(adjustedSpeedJustAboveLowerErrorMargin).isEqualTo(1f);
|
||||
assertThat(adjustedSpeedJustBelowUpperErrorMargin).isEqualTo(1f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustPlaybackSpeed_withLiveOffsetGreaterThanTargetOffset_returnsAdjustedSpeed() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().setProportionalControlFactor(0.01f).build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
|
||||
float adjustedSpeed =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
|
||||
|
||||
float expectedSpeedAccordingToDocumentation = 1f + 0.01f * (2.5f - 2f);
|
||||
assertThat(adjustedSpeed).isEqualTo(expectedSpeedAccordingToDocumentation);
|
||||
assertThat(adjustedSpeed).isGreaterThan(1f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustPlaybackSpeed_withLiveOffsetLowerThanTargetOffset_returnsAdjustedSpeed() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().setProportionalControlFactor(0.01f).build();
|
||||
defaultLivePlaybackSpeedControl.overrideTargetLiveOffsetUs(2_000_000);
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
|
||||
float adjustedSpeed =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
|
||||
|
||||
float expectedSpeedAccordingToDocumentation = 1f + 0.01f * (1.5f - 2f);
|
||||
assertThat(adjustedSpeed).isEqualTo(expectedSpeedAccordingToDocumentation);
|
||||
assertThat(adjustedSpeed).isLessThan(1f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustPlaybackSpeed_withLiveOffsetGreaterThanTargetOffset_clampedToFallbackMaximumSpeed() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().setFallbackMaxPlaybackSpeed(1.5f).build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
|
||||
float adjustedSpeed =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 999_999_999_999L);
|
||||
|
||||
assertThat(adjustedSpeed).isEqualTo(1.5f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustPlaybackSpeed_withLiveOffsetLowerThanTargetOffset_clampedToFallbackMinimumSpeed() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().setFallbackMinPlaybackSpeed(0.5f).build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
|
||||
float adjustedSpeed =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ -999_999_999_999L);
|
||||
|
||||
assertThat(adjustedSpeed).isEqualTo(0.5f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustPlaybackSpeed_andMediaProvidedMaxSpeedWithLiveOffsetGreaterThanTargetOffset_clampedToMediaMaxSpeed() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().setFallbackMaxPlaybackSpeed(1.5f).build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ 2f));
|
||||
|
||||
float adjustedSpeed =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 999_999_999_999L);
|
||||
|
||||
assertThat(adjustedSpeed).isEqualTo(2f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustPlaybackSpeed_andMediaProvidedMinSpeedWithLiveOffsetLowerThanTargetOffset_clampedToMediaMinSpeed() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().setFallbackMinPlaybackSpeed(0.5f).build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ 0.2f,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
|
||||
float adjustedSpeed =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ -999_999_999_999L);
|
||||
|
||||
assertThat(adjustedSpeed).isEqualTo(0.2f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustPlaybackSpeed_repeatedCallWithinMinUpdateInterval_returnsSameAdjustedSpeed() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().setMinUpdateIntervalMs(123).build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
|
||||
float adjustedSpeed1 =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
|
||||
ShadowSystemClock.advanceBy(Duration.ofMillis(122));
|
||||
float adjustedSpeed2 =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
|
||||
ShadowSystemClock.advanceBy(Duration.ofMillis(2));
|
||||
float adjustedSpeed3 =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
|
||||
|
||||
assertThat(adjustedSpeed1).isEqualTo(adjustedSpeed2);
|
||||
assertThat(adjustedSpeed3).isNotEqualTo(adjustedSpeed2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustPlaybackSpeed_repeatedCallAfterUpdateLiveConfiguration_updatesSpeedAgain() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().setMinUpdateIntervalMs(123).build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
|
||||
float adjustedSpeed1 =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
float adjustedSpeed2 =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
|
||||
|
||||
assertThat(adjustedSpeed1).isNotEqualTo(adjustedSpeed2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustPlaybackSpeed_repeatedCallAfterNewTargetLiveOffset_updatesSpeedAgain() {
|
||||
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
|
||||
new DefaultLivePlaybackSpeedControl.Builder().setMinUpdateIntervalMs(123).build();
|
||||
defaultLivePlaybackSpeedControl.updateLiveConfiguration(
|
||||
new LiveConfiguration(
|
||||
/* targetLiveOffsetMs= */ 2_000,
|
||||
/* minPlaybackSpeed= */ C.RATE_UNSET,
|
||||
/* maxPlaybackSpeed= */ C.RATE_UNSET));
|
||||
|
||||
float adjustedSpeed1 =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 1_500_000);
|
||||
defaultLivePlaybackSpeedControl.overrideTargetLiveOffsetUs(2_000_001);
|
||||
float adjustedSpeed2 =
|
||||
defaultLivePlaybackSpeedControl.adjustPlaybackSpeed(/* liveOffsetUs= */ 2_500_000);
|
||||
|
||||
assertThat(adjustedSpeed1).isNotEqualTo(adjustedSpeed2);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user