mirror of
https://github.com/androidx/media.git
synced 2025-05-15 11:39:56 +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