Forward live playback speed changes to track selections and renderers.

This allows the respective components to adapt to the speed changes
if desired.

To avoid frequent updates to the media codec operating rate, we also
forward the target speed to the renderers so that this value can be set
based on the target speed and not the current speed.

PiperOrigin-RevId: 351336401
This commit is contained in:
tonihei 2021-01-12 11:22:50 +00:00 committed by Oliver Woodman
parent c63f3d92ba
commit d640cedab8
6 changed files with 65 additions and 42 deletions

View File

@ -447,7 +447,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
@Override @Override
public void onPlaybackParametersChanged(PlaybackParameters newPlaybackParameters) { public void onPlaybackParametersChanged(PlaybackParameters newPlaybackParameters) {
sendPlaybackParametersChangedInternal(newPlaybackParameters, /* acknowledgeCommand= */ false); handler
.obtainMessage(MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL, newPlaybackParameters)
.sendToTarget();
} }
// Handler.Callback implementation. // Handler.Callback implementation.
@ -501,8 +503,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
reselectTracksInternal(); reselectTracksInternal();
break; break;
case MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL: case MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL:
handlePlaybackParameters( handlePlaybackParameters((PlaybackParameters) msg.obj, /* acknowledgeCommand= */ false);
(PlaybackParameters) msg.obj, /* acknowledgeCommand= */ msg.arg1 != 0);
break; break;
case MSG_SEND_MESSAGE: case MSG_SEND_MESSAGE:
sendMessageInternal((PlayerMessage) msg.obj); sendMessageInternal((PlayerMessage) msg.obj);
@ -888,6 +889,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
getCurrentLiveOffsetUs(), getTotalBufferedDurationUs()); getCurrentLiveOffsetUs(), getTotalBufferedDurationUs());
if (mediaClock.getPlaybackParameters().speed != adjustedSpeed) { if (mediaClock.getPlaybackParameters().speed != adjustedSpeed) {
mediaClock.setPlaybackParameters(playbackInfo.playbackParameters.withSpeed(adjustedSpeed)); mediaClock.setPlaybackParameters(playbackInfo.playbackParameters.withSpeed(adjustedSpeed));
handlePlaybackParameters(
playbackInfo.playbackParameters,
/* currentPlaybackSpeed= */ mediaClock.getPlaybackParameters().speed,
/* updatePlaybackInfo= */ false,
/* acknowledgeCommand= */ false);
} }
} }
} }
@ -1279,10 +1285,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
notifyTrackSelectionDiscontinuity(); notifyTrackSelectionDiscontinuity();
} }
private void setPlaybackParametersInternal(PlaybackParameters playbackParameters) { private void setPlaybackParametersInternal(PlaybackParameters playbackParameters)
throws ExoPlaybackException {
mediaClock.setPlaybackParameters(playbackParameters); mediaClock.setPlaybackParameters(playbackParameters);
sendPlaybackParametersChangedInternal( handlePlaybackParameters(mediaClock.getPlaybackParameters(), /* acknowledgeCommand= */ true);
mediaClock.getPlaybackParameters(), /* acknowledgeCommand= */ true);
} }
private void setSeekParametersInternal(SeekParameters seekParameters) { private void setSeekParametersInternal(SeekParameters seekParameters) {
@ -2141,12 +2147,30 @@ import java.util.concurrent.atomic.AtomicBoolean;
private void handlePlaybackParameters( private void handlePlaybackParameters(
PlaybackParameters playbackParameters, boolean acknowledgeCommand) PlaybackParameters playbackParameters, boolean acknowledgeCommand)
throws ExoPlaybackException { throws ExoPlaybackException {
playbackInfoUpdate.incrementPendingOperationAcks(acknowledgeCommand ? 1 : 0); handlePlaybackParameters(
playbackInfo = playbackInfo.copyWithPlaybackParameters(playbackParameters); playbackParameters,
playbackParameters.speed,
/* updatePlaybackInfo= */ true,
acknowledgeCommand);
}
private void handlePlaybackParameters(
PlaybackParameters playbackParameters,
float currentPlaybackSpeed,
boolean updatePlaybackInfo,
boolean acknowledgeCommand)
throws ExoPlaybackException {
if (updatePlaybackInfo) {
if (acknowledgeCommand) {
playbackInfoUpdate.incrementPendingOperationAcks(1);
}
playbackInfo = playbackInfo.copyWithPlaybackParameters(playbackParameters);
}
updateTrackSelectionPlaybackSpeed(playbackParameters.speed); updateTrackSelectionPlaybackSpeed(playbackParameters.speed);
for (Renderer renderer : renderers) { for (Renderer renderer : renderers) {
if (renderer != null) { if (renderer != null) {
renderer.setPlaybackSpeed(playbackParameters.speed); renderer.setPlaybackSpeed(
currentPlaybackSpeed, /* targetPlaybackSpeed= */ playbackParameters.speed);
} }
} }
} }
@ -2372,17 +2396,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
loadControl.onTracksSelected(renderers, trackGroups, trackSelectorResult.selections); loadControl.onTracksSelected(renderers, trackGroups, trackSelectorResult.selections);
} }
private void sendPlaybackParametersChangedInternal(
PlaybackParameters playbackParameters, boolean acknowledgeCommand) {
handler
.obtainMessage(
MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL,
acknowledgeCommand ? 1 : 0,
0,
playbackParameters)
.sendToTarget();
}
private boolean shouldPlayWhenReady() { private boolean shouldPlayWhenReady() {
return playbackInfo.playWhenReady return playbackInfo.playWhenReady
&& playbackInfo.playbackSuppressionReason == Player.PLAYBACK_SUPPRESSION_REASON_NONE; && playbackInfo.playbackSuppressionReason == Player.PLAYBACK_SUPPRESSION_REASON_NONE;

View File

@ -403,10 +403,14 @@ public interface Renderer extends PlayerMessage.Target {
* *
* <p>The default implementation is a no-op. * <p>The default implementation is a no-op.
* *
* @param playbackSpeed The factor by which playback is sped up. * @param currentPlaybackSpeed The factor by which playback is currently sped up.
* @param targetPlaybackSpeed The target factor by which playback should be sped up. This may be
* different from {@code currentPlaybackSpeed}, for example, if the speed is temporarily
* adjusted for live playback.
* @throws ExoPlaybackException If an error occurs handling the playback speed. * @throws ExoPlaybackException If an error occurs handling the playback speed.
*/ */
default void setPlaybackSpeed(float playbackSpeed) throws ExoPlaybackException {} default void setPlaybackSpeed(float currentPlaybackSpeed, float targetPlaybackSpeed)
throws ExoPlaybackException {}
/** /**
* Incrementally renders the {@link SampleStream}. * Incrementally renders the {@link SampleStream}.

View File

@ -390,7 +390,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override @Override
protected float getCodecOperatingRateV23( protected float getCodecOperatingRateV23(
float playbackSpeed, Format format, Format[] streamFormats) { float targetPlaybackSpeed, Format format, Format[] streamFormats) {
// Use the highest known stream sample-rate up front, to avoid having to reconfigure the codec // Use the highest known stream sample-rate up front, to avoid having to reconfigure the codec
// should an adaptive switch to that stream occur. // should an adaptive switch to that stream occur.
int maxSampleRate = -1; int maxSampleRate = -1;
@ -400,7 +400,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
maxSampleRate = max(maxSampleRate, streamSampleRate); maxSampleRate = max(maxSampleRate, streamSampleRate);
} }
} }
return maxSampleRate == -1 ? CODEC_OPERATING_RATE_UNSET : (maxSampleRate * playbackSpeed); return maxSampleRate == -1 ? CODEC_OPERATING_RATE_UNSET : (maxSampleRate * targetPlaybackSpeed);
} }
@Override @Override

View File

@ -312,7 +312,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
@Nullable private MediaCrypto mediaCrypto; @Nullable private MediaCrypto mediaCrypto;
private boolean mediaCryptoRequiresSecureDecoder; private boolean mediaCryptoRequiresSecureDecoder;
private long renderTimeLimitMs; private long renderTimeLimitMs;
private float playbackSpeed; private float currentPlaybackSpeed;
private float targetPlaybackSpeed;
@Nullable private MediaCodecAdapter codec; @Nullable private MediaCodecAdapter codec;
@Nullable private Format codecInputFormat; @Nullable private Format codecInputFormat;
@Nullable private MediaFormat codecOutputMediaFormat; @Nullable private MediaFormat codecOutputMediaFormat;
@ -393,7 +394,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
formatQueue = new TimedValueQueue<>(); formatQueue = new TimedValueQueue<>();
decodeOnlyPresentationTimestamps = new ArrayList<>(); decodeOnlyPresentationTimestamps = new ArrayList<>();
outputBufferInfo = new MediaCodec.BufferInfo(); outputBufferInfo = new MediaCodec.BufferInfo();
playbackSpeed = 1f; currentPlaybackSpeed = 1f;
targetPlaybackSpeed = 1f;
renderTimeLimitMs = C.TIME_UNSET; renderTimeLimitMs = C.TIME_UNSET;
pendingOutputStreamStartPositionsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; pendingOutputStreamStartPositionsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
@ -715,8 +717,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
@Override @Override
public void setPlaybackSpeed(float playbackSpeed) throws ExoPlaybackException { public void setPlaybackSpeed(float currentPlaybackSpeed, float targetPlaybackSpeed)
this.playbackSpeed = playbackSpeed; throws ExoPlaybackException {
this.currentPlaybackSpeed = currentPlaybackSpeed;
this.targetPlaybackSpeed = targetPlaybackSpeed;
if (codec != null if (codec != null
&& codecDrainAction != DRAIN_ACTION_REINITIALIZE && codecDrainAction != DRAIN_ACTION_REINITIALIZE
&& getState() != STATE_DISABLED) { && getState() != STATE_DISABLED) {
@ -1082,7 +1086,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
float codecOperatingRate = float codecOperatingRate =
Util.SDK_INT < 23 Util.SDK_INT < 23
? CODEC_OPERATING_RATE_UNSET ? CODEC_OPERATING_RATE_UNSET
: getCodecOperatingRateV23(playbackSpeed, inputFormat, getStreamFormats()); : getCodecOperatingRateV23(targetPlaybackSpeed, inputFormat, getStreamFormats());
if (codecOperatingRate <= assumedMinimumCodecOperatingRate) { if (codecOperatingRate <= assumedMinimumCodecOperatingRate) {
codecOperatingRate = CODEC_OPERATING_RATE_UNSET; codecOperatingRate = CODEC_OPERATING_RATE_UNSET;
} }
@ -1624,9 +1628,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
&& SystemClock.elapsedRealtime() < codecHotswapDeadlineMs)); && SystemClock.elapsedRealtime() < codecHotswapDeadlineMs));
} }
/** Returns the playback speed, as set by {@link #setPlaybackSpeed}. */ /** Returns the current playback speed, as set by {@link #setPlaybackSpeed}. */
protected float getPlaybackSpeed() { protected float getPlaybackSpeed() {
return playbackSpeed; return currentPlaybackSpeed;
} }
/** Returns the operating rate used by the current codec */ /** Returns the operating rate used by the current codec */
@ -1640,14 +1644,16 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* *
* <p>The default implementation returns {@link #CODEC_OPERATING_RATE_UNSET}. * <p>The default implementation returns {@link #CODEC_OPERATING_RATE_UNSET}.
* *
* @param playbackSpeed The factor by which playback is sped up. * @param targetPlaybackSpeed The target factor by which playback should be sped up. This may be
* different from the current playback speed, for example, if the speed is temporarily
* adjusted for live playback.
* @param format The {@link Format} for which the codec is being configured. * @param format The {@link Format} for which the codec is being configured.
* @param streamFormats The possible stream formats. * @param streamFormats The possible stream formats.
* @return The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if no codec operating * @return The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if no codec operating
* rate should be set. * rate should be set.
*/ */
protected float getCodecOperatingRateV23( protected float getCodecOperatingRateV23(
float playbackSpeed, Format format, Format[] streamFormats) { float targetPlaybackSpeed, Format format, Format[] streamFormats) {
return CODEC_OPERATING_RATE_UNSET; return CODEC_OPERATING_RATE_UNSET;
} }
@ -1665,7 +1671,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
float newCodecOperatingRate = float newCodecOperatingRate =
getCodecOperatingRateV23(playbackSpeed, format, getStreamFormats()); getCodecOperatingRateV23(targetPlaybackSpeed, format, getStreamFormats());
if (codecOperatingRate == newCodecOperatingRate) { if (codecOperatingRate == newCodecOperatingRate) {
// No change. // No change.
return true; return true;

View File

@ -645,14 +645,15 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
} }
@Override @Override
public void setPlaybackSpeed(float playbackSpeed) throws ExoPlaybackException { public void setPlaybackSpeed(float currentPlaybackSpeed, float targetPlaybackSpeed)
super.setPlaybackSpeed(playbackSpeed); throws ExoPlaybackException {
frameReleaseHelper.onPlaybackSpeed(playbackSpeed); super.setPlaybackSpeed(currentPlaybackSpeed, targetPlaybackSpeed);
frameReleaseHelper.onPlaybackSpeed(currentPlaybackSpeed);
} }
@Override @Override
protected float getCodecOperatingRateV23( protected float getCodecOperatingRateV23(
float playbackSpeed, Format format, Format[] streamFormats) { float targetPlaybackSpeed, Format format, Format[] streamFormats) {
// Use the highest known stream frame-rate up front, to avoid having to reconfigure the codec // Use the highest known stream frame-rate up front, to avoid having to reconfigure the codec
// should an adaptive switch to that stream occur. // should an adaptive switch to that stream occur.
float maxFrameRate = -1; float maxFrameRate = -1;
@ -662,7 +663,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
maxFrameRate = max(maxFrameRate, streamFrameRate); maxFrameRate = max(maxFrameRate, streamFrameRate);
} }
} }
return maxFrameRate == -1 ? CODEC_OPERATING_RATE_UNSET : (maxFrameRate * playbackSpeed); return maxFrameRate == -1 ? CODEC_OPERATING_RATE_UNSET : (maxFrameRate * targetPlaybackSpeed);
} }
@Override @Override

View File

@ -190,10 +190,9 @@ public final class VideoFrameReleaseHelper {
} }
/** /**
* Called when the renderer's playback speed changes, where 1 is the default rate, 2 is twice the * Called when the renderer's playback speed changes.
* default rate, 0.5 is half the default rate and so on.
* *
* @param playbackSpeed The player's speed. * @param playbackSpeed The factor by which playback is sped up.
*/ */
public void onPlaybackSpeed(float playbackSpeed) { public void onPlaybackSpeed(float playbackSpeed) {
this.playbackSpeed = playbackSpeed; this.playbackSpeed = playbackSpeed;