mirror of https://github.com/NekoX-Dev/NekoX.git
459 lines
19 KiB
Java
459 lines
19 KiB
Java
/*
|
|
* 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.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;
|
|
import com.google.android.exoplayer2.util.Util;
|
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|
|
|
/**
|
|
* 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 closer to {@code targetLiveOffsetSec} than the value set with {@link
|
|
* Builder#setMaxLiveOffsetErrorMsForUnitSpeed(long)}.
|
|
*
|
|
* <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.
|
|
*
|
|
* <p>When the player rebuffers, the target live offset {@link
|
|
* Builder#setTargetLiveOffsetIncrementOnRebufferMs(long) is increased} to adjust to the reduced
|
|
* 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 {
|
|
|
|
/**
|
|
* The default minimum factor by which playback can be sped up 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 factor by which playback can be sped up 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 = 1_000;
|
|
|
|
/**
|
|
* The default {@link Builder#setProportionalControlFactor(float) proportional control factor}
|
|
* used to adjust the playback speed.
|
|
*/
|
|
public static final float DEFAULT_PROPORTIONAL_CONTROL_FACTOR = 0.1f;
|
|
|
|
/**
|
|
* The default increment applied to the target live offset each time the player is rebuffering, in
|
|
* milliseconds
|
|
*/
|
|
public static final long DEFAULT_TARGET_LIVE_OFFSET_INCREMENT_ON_REBUFFER_MS = 500;
|
|
|
|
/**
|
|
* The 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 default maximum difference between the current live offset and the target live offset, in
|
|
* milliseconds, for which unit speed (1.0f) is used.
|
|
*/
|
|
public static final long DEFAULT_MAX_LIVE_OFFSET_ERROR_MS_FOR_UNIT_SPEED = 20;
|
|
|
|
/** Builder for a {@link DefaultLivePlaybackSpeedControl}. */
|
|
public static final class Builder {
|
|
|
|
private float fallbackMinPlaybackSpeed;
|
|
private float fallbackMaxPlaybackSpeed;
|
|
private long minUpdateIntervalMs;
|
|
private float proportionalControlFactorUs;
|
|
private long maxLiveOffsetErrorUsForUnitSpeed;
|
|
private long targetLiveOffsetIncrementOnRebufferUs;
|
|
private float minPossibleLiveOffsetSmoothingFactor;
|
|
|
|
/** 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;
|
|
maxLiveOffsetErrorUsForUnitSpeed =
|
|
Util.msToUs(DEFAULT_MAX_LIVE_OFFSET_ERROR_MS_FOR_UNIT_SPEED);
|
|
targetLiveOffsetIncrementOnRebufferUs =
|
|
Util.msToUs(DEFAULT_TARGET_LIVE_OFFSET_INCREMENT_ON_REBUFFER_MS);
|
|
minPossibleLiveOffsetSmoothingFactor = DEFAULT_MIN_POSSIBLE_LIVE_OFFSET_SMOOTHING_FACTOR;
|
|
}
|
|
|
|
/**
|
|
* 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 factor by which playback can be sped up.
|
|
* @return This builder, for convenience.
|
|
*/
|
|
@CanIgnoreReturnValue
|
|
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 factor by which playback can be sped up.
|
|
* @return This builder, for convenience.
|
|
*/
|
|
@CanIgnoreReturnValue
|
|
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.
|
|
*/
|
|
@CanIgnoreReturnValue
|
|
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 factor by which playback will be sped up 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.
|
|
*/
|
|
@CanIgnoreReturnValue
|
|
public Builder setProportionalControlFactor(float proportionalControlFactor) {
|
|
Assertions.checkArgument(proportionalControlFactor > 0);
|
|
this.proportionalControlFactorUs = proportionalControlFactor / C.MICROS_PER_SECOND;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the maximum difference between the current live offset and the target live offset, in
|
|
* milliseconds, for which unit speed (1.0f) is used.
|
|
*
|
|
* <p>The default is {@link #DEFAULT_MAX_LIVE_OFFSET_ERROR_MS_FOR_UNIT_SPEED}.
|
|
*
|
|
* @param maxLiveOffsetErrorMsForUnitSpeed The maximum live offset error for which unit speed is
|
|
* used, in milliseconds.
|
|
* @return This builder, for convenience.
|
|
*/
|
|
@CanIgnoreReturnValue
|
|
public Builder setMaxLiveOffsetErrorMsForUnitSpeed(long maxLiveOffsetErrorMsForUnitSpeed) {
|
|
Assertions.checkArgument(maxLiveOffsetErrorMsForUnitSpeed > 0);
|
|
this.maxLiveOffsetErrorUsForUnitSpeed = Util.msToUs(maxLiveOffsetErrorMsForUnitSpeed);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the increment applied to the target live offset each time the player is rebuffering, in
|
|
* milliseconds.
|
|
*
|
|
* @param targetLiveOffsetIncrementOnRebufferMs The increment applied to the target live offset
|
|
* when the player is rebuffering, in milliseconds
|
|
* @return This builder, for convenience.
|
|
*/
|
|
@CanIgnoreReturnValue
|
|
public Builder setTargetLiveOffsetIncrementOnRebufferMs(
|
|
long targetLiveOffsetIncrementOnRebufferMs) {
|
|
Assertions.checkArgument(targetLiveOffsetIncrementOnRebufferMs >= 0);
|
|
this.targetLiveOffsetIncrementOnRebufferUs =
|
|
Util.msToUs(targetLiveOffsetIncrementOnRebufferMs);
|
|
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 ≥ 0 and < 1.
|
|
* @return This builder, for convenience.
|
|
*/
|
|
@CanIgnoreReturnValue
|
|
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(
|
|
fallbackMinPlaybackSpeed,
|
|
fallbackMaxPlaybackSpeed,
|
|
minUpdateIntervalMs,
|
|
proportionalControlFactorUs,
|
|
maxLiveOffsetErrorUsForUnitSpeed,
|
|
targetLiveOffsetIncrementOnRebufferUs,
|
|
minPossibleLiveOffsetSmoothingFactor);
|
|
}
|
|
}
|
|
|
|
private final float fallbackMinPlaybackSpeed;
|
|
private final float fallbackMaxPlaybackSpeed;
|
|
private final long minUpdateIntervalMs;
|
|
private final float proportionalControlFactor;
|
|
private final long maxLiveOffsetErrorUsForUnitSpeed;
|
|
private final long targetLiveOffsetRebufferDeltaUs;
|
|
private final float minPossibleLiveOffsetSmoothingFactor;
|
|
|
|
private long mediaConfigurationTargetLiveOffsetUs;
|
|
private long targetLiveOffsetOverrideUs;
|
|
private long idealTargetLiveOffsetUs;
|
|
private long minTargetLiveOffsetUs;
|
|
private long maxTargetLiveOffsetUs;
|
|
private long currentTargetLiveOffsetUs;
|
|
|
|
private float maxPlaybackSpeed;
|
|
private float minPlaybackSpeed;
|
|
private float adjustedPlaybackSpeed;
|
|
private long lastPlaybackSpeedUpdateMs;
|
|
|
|
private long smoothedMinPossibleLiveOffsetUs;
|
|
private long smoothedMinPossibleLiveOffsetDeviationUs;
|
|
|
|
private DefaultLivePlaybackSpeedControl(
|
|
float fallbackMinPlaybackSpeed,
|
|
float fallbackMaxPlaybackSpeed,
|
|
long minUpdateIntervalMs,
|
|
float proportionalControlFactor,
|
|
long maxLiveOffsetErrorUsForUnitSpeed,
|
|
long targetLiveOffsetRebufferDeltaUs,
|
|
float minPossibleLiveOffsetSmoothingFactor) {
|
|
this.fallbackMinPlaybackSpeed = fallbackMinPlaybackSpeed;
|
|
this.fallbackMaxPlaybackSpeed = fallbackMaxPlaybackSpeed;
|
|
this.minUpdateIntervalMs = minUpdateIntervalMs;
|
|
this.proportionalControlFactor = proportionalControlFactor;
|
|
this.maxLiveOffsetErrorUsForUnitSpeed = maxLiveOffsetErrorUsForUnitSpeed;
|
|
this.targetLiveOffsetRebufferDeltaUs = targetLiveOffsetRebufferDeltaUs;
|
|
this.minPossibleLiveOffsetSmoothingFactor = minPossibleLiveOffsetSmoothingFactor;
|
|
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;
|
|
idealTargetLiveOffsetUs = C.TIME_UNSET;
|
|
currentTargetLiveOffsetUs = C.TIME_UNSET;
|
|
smoothedMinPossibleLiveOffsetUs = C.TIME_UNSET;
|
|
smoothedMinPossibleLiveOffsetDeviationUs = C.TIME_UNSET;
|
|
}
|
|
|
|
@Override
|
|
public void setLiveConfiguration(LiveConfiguration liveConfiguration) {
|
|
mediaConfigurationTargetLiveOffsetUs = Util.msToUs(liveConfiguration.targetOffsetMs);
|
|
minTargetLiveOffsetUs = Util.msToUs(liveConfiguration.minOffsetMs);
|
|
maxTargetLiveOffsetUs = Util.msToUs(liveConfiguration.maxOffsetMs);
|
|
minPlaybackSpeed =
|
|
liveConfiguration.minPlaybackSpeed != C.RATE_UNSET
|
|
? liveConfiguration.minPlaybackSpeed
|
|
: fallbackMinPlaybackSpeed;
|
|
maxPlaybackSpeed =
|
|
liveConfiguration.maxPlaybackSpeed != C.RATE_UNSET
|
|
? liveConfiguration.maxPlaybackSpeed
|
|
: fallbackMaxPlaybackSpeed;
|
|
if (minPlaybackSpeed == 1f && maxPlaybackSpeed == 1f) {
|
|
// Don't bother calculating adjustments if it's not possible to change the speed.
|
|
mediaConfigurationTargetLiveOffsetUs = C.TIME_UNSET;
|
|
}
|
|
maybeResetTargetLiveOffsetUs();
|
|
}
|
|
|
|
@Override
|
|
public void setTargetLiveOffsetOverrideUs(long liveOffsetUs) {
|
|
targetLiveOffsetOverrideUs = liveOffsetUs;
|
|
maybeResetTargetLiveOffsetUs();
|
|
}
|
|
|
|
@Override
|
|
public void notifyRebuffer() {
|
|
if (currentTargetLiveOffsetUs == C.TIME_UNSET) {
|
|
return;
|
|
}
|
|
currentTargetLiveOffsetUs += targetLiveOffsetRebufferDeltaUs;
|
|
if (maxTargetLiveOffsetUs != C.TIME_UNSET
|
|
&& currentTargetLiveOffsetUs > maxTargetLiveOffsetUs) {
|
|
currentTargetLiveOffsetUs = maxTargetLiveOffsetUs;
|
|
}
|
|
lastPlaybackSpeedUpdateMs = C.TIME_UNSET;
|
|
}
|
|
|
|
@Override
|
|
public float getAdjustedPlaybackSpeed(long liveOffsetUs, 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) < maxLiveOffsetErrorUsForUnitSpeed) {
|
|
adjustedPlaybackSpeed = 1f;
|
|
} else {
|
|
float calculatedSpeed = 1f + proportionalControlFactor * liveOffsetErrorUs;
|
|
adjustedPlaybackSpeed =
|
|
Util.constrainValue(calculatedSpeed, minPlaybackSpeed, maxPlaybackSpeed);
|
|
}
|
|
return adjustedPlaybackSpeed;
|
|
}
|
|
|
|
@Override
|
|
public long getTargetLiveOffsetUs() {
|
|
return currentTargetLiveOffsetUs;
|
|
}
|
|
|
|
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 (idealTargetLiveOffsetUs == idealOffsetUs) {
|
|
return;
|
|
}
|
|
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 = Util.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);
|
|
}
|
|
}
|