Commit 74c493f5 by andrewlewis Committed by Oliver Woodman

Add back support for setting audio pitch

To play slow motion streams where the audio has been recorded at
slower speeds, it is necessary to be able to resample (rather than
time-stretch) the audio. This change undeprecates back the previously
deprecated PlaybackParameters class to allow apps to set pitch.

PiperOrigin-RevId: 328703116
parent 8b3422ba
Showing with 321 additions and 343 deletions
......@@ -458,33 +458,17 @@ public final class CastPlayer extends BasePlayer {
flushNotifications();
}
/** @deprecated Use {@link #setPlaybackSpeed(float)} instead. */
@SuppressWarnings("deprecation")
@Deprecated
@Override
public void setPlaybackParameters(@Nullable PlaybackParameters playbackParameters) {
// Unsupported by the RemoteMediaClient API. Do nothing.
}
/** @deprecated Use {@link #getPlaybackSpeed()} instead. */
@SuppressWarnings("deprecation")
@Deprecated
@Override
public PlaybackParameters getPlaybackParameters() {
return PlaybackParameters.DEFAULT;
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {
// Unsupported by the RemoteMediaClient API. Do nothing.
}
@Override
public float getPlaybackSpeed() {
return Player.DEFAULT_PLAYBACK_SPEED;
}
@Override
public void stop(boolean reset) {
playbackState = STATE_IDLE;
if (remoteMediaClient != null) {
......
......@@ -26,6 +26,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ControlDispatcher;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioAttributes;
......@@ -383,11 +384,11 @@ import java.util.List;
}
public void setPlaybackSpeed(float playbackSpeed) {
player.setPlaybackSpeed(playbackSpeed);
player.setPlaybackParameters(new PlaybackParameters(playbackSpeed));
}
public float getPlaybackSpeed() {
return player.getPlaybackSpeed();
return player.getPlaybackParameters().speed;
}
public void reset() {
......@@ -483,8 +484,8 @@ import java.util.List;
listener.onShuffleModeChanged(Utils.getShuffleMode(shuffleModeEnabled));
}
private void handlePlaybackSpeedChanged(float playbackSpeed) {
listener.onPlaybackSpeedChanged(playbackSpeed);
private void handlePlaybackParametersChanged(PlaybackParameters playbackParameters) {
listener.onPlaybackSpeedChanged(playbackParameters.speed);
}
private void handleTimelineChanged(Timeline timeline) {
......@@ -627,8 +628,8 @@ import java.util.List;
}
@Override
public void onPlaybackSpeedChanged(float playbackSpeed) {
handlePlaybackSpeedChanged(playbackSpeed);
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
handlePlaybackParametersChanged(playbackParameters);
}
@Override
......
......@@ -38,6 +38,7 @@ import com.google.android.exoplayer2.ControlDispatcher;
import com.google.android.exoplayer2.DefaultControlDispatcher;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.util.Assertions;
......@@ -127,8 +128,8 @@ public final class MediaSessionConnector {
@PlaybackActions public static final long DEFAULT_PLAYBACK_ACTIONS = ALL_PLAYBACK_ACTIONS;
/**
* The name of the {@link PlaybackStateCompat} float extra with the value of {@link
* Player#getPlaybackSpeed()}.
* The name of the {@link PlaybackStateCompat} float extra with the value of {@code
* Player.getPlaybackParameters().speed}.
*/
public static final String EXTRAS_SPEED = "EXO_SPEED";
......@@ -765,7 +766,7 @@ public final class MediaSessionConnector {
queueNavigator != null
? queueNavigator.getActiveQueueItemId(player)
: MediaSessionCompat.QueueItem.UNKNOWN_ID;
float playbackSpeed = player.getPlaybackSpeed();
float playbackSpeed = player.getPlaybackParameters().speed;
extras.putFloat(EXTRAS_SPEED, playbackSpeed);
float sessionPlaybackSpeed = player.isPlaying() ? playbackSpeed : 0f;
builder
......@@ -1134,7 +1135,7 @@ public final class MediaSessionConnector {
}
@Override
public void onPlaybackSpeedChanged(float playbackSpeed) {
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
invalidateMediaSessionPlaybackState();
}
......
......@@ -27,20 +27,20 @@ import com.google.android.exoplayer2.util.StandaloneMediaClock;
*/
/* package */ final class DefaultMediaClock implements MediaClock {
/** Listener interface to be notified of changes to the active playback speed. */
public interface PlaybackSpeedListener {
/** Listener interface to be notified of changes to the active playback parameters. */
public interface PlaybackParametersListener {
/**
* Called when the active playback speed changed. Will not be called for {@link
* #setPlaybackSpeed(float)}.
* Called when the active playback parameters changed. Will not be called for {@link
* #setPlaybackParameters(PlaybackParameters)}.
*
* @param newPlaybackSpeed The newly active playback speed.
* @param newPlaybackParameters The newly active playback parameters.
*/
void onPlaybackSpeedChanged(float newPlaybackSpeed);
void onPlaybackParametersChanged(PlaybackParameters newPlaybackParameters);
}
private final StandaloneMediaClock standaloneClock;
private final PlaybackSpeedListener listener;
private final PlaybackParametersListener listener;
@Nullable private Renderer rendererClockSource;
@Nullable private MediaClock rendererClock;
......@@ -48,13 +48,13 @@ import com.google.android.exoplayer2.util.StandaloneMediaClock;
private boolean standaloneClockIsStarted;
/**
* Creates a new instance with listener for playback speed changes and a {@link Clock} to use for
* the standalone clock implementation.
* Creates a new instance with a listener for playback parameters changes and a {@link Clock} to
* use for the standalone clock implementation.
*
* @param listener A {@link PlaybackSpeedListener} to listen for playback speed changes.
* @param listener A {@link PlaybackParametersListener} to listen for playback parameters changes.
* @param clock A {@link Clock}.
*/
public DefaultMediaClock(PlaybackSpeedListener listener, Clock clock) {
public DefaultMediaClock(PlaybackParametersListener listener, Clock clock) {
this.listener = listener;
this.standaloneClock = new StandaloneMediaClock(clock);
isUsingStandaloneClock = true;
......@@ -102,7 +102,7 @@ import com.google.android.exoplayer2.util.StandaloneMediaClock;
}
this.rendererClock = rendererMediaClock;
this.rendererClockSource = renderer;
rendererClock.setPlaybackSpeed(standaloneClock.getPlaybackSpeed());
rendererClock.setPlaybackParameters(standaloneClock.getPlaybackParameters());
}
}
......@@ -140,19 +140,19 @@ import com.google.android.exoplayer2.util.StandaloneMediaClock;
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {
public void setPlaybackParameters(PlaybackParameters playbackParameters) {
if (rendererClock != null) {
rendererClock.setPlaybackSpeed(playbackSpeed);
playbackSpeed = rendererClock.getPlaybackSpeed();
rendererClock.setPlaybackParameters(playbackParameters);
playbackParameters = rendererClock.getPlaybackParameters();
}
standaloneClock.setPlaybackSpeed(playbackSpeed);
standaloneClock.setPlaybackParameters(playbackParameters);
}
@Override
public float getPlaybackSpeed() {
public PlaybackParameters getPlaybackParameters() {
return rendererClock != null
? rendererClock.getPlaybackSpeed()
: standaloneClock.getPlaybackSpeed();
? rendererClock.getPlaybackParameters()
: standaloneClock.getPlaybackParameters();
}
private void syncClocks(boolean isReadingAhead) {
......@@ -180,10 +180,10 @@ import com.google.android.exoplayer2.util.StandaloneMediaClock;
}
// Continuously sync stand-alone clock to renderer clock so that it can take over if needed.
standaloneClock.resetPosition(rendererClockPositionUs);
float playbackSpeed = rendererClock.getPlaybackSpeed();
if (playbackSpeed != standaloneClock.getPlaybackSpeed()) {
standaloneClock.setPlaybackSpeed(playbackSpeed);
listener.onPlaybackSpeedChanged(playbackSpeed);
PlaybackParameters playbackParameters = rendererClock.getPlaybackParameters();
if (!playbackParameters.equals(standaloneClock.getPlaybackParameters())) {
standaloneClock.setPlaybackParameters(playbackParameters);
listener.onPlaybackParametersChanged(playbackParameters);
}
}
......
......@@ -619,32 +619,17 @@ import java.util.concurrent.TimeoutException;
/* seekProcessed= */ true);
}
/** @deprecated Use {@link #setPlaybackSpeed(float)} instead. */
@SuppressWarnings("deprecation")
@Deprecated
@Override
public void setPlaybackParameters(@Nullable PlaybackParameters playbackParameters) {
setPlaybackSpeed(
playbackParameters != null ? playbackParameters.speed : Player.DEFAULT_PLAYBACK_SPEED);
}
/** @deprecated Use {@link #getPlaybackSpeed()} instead. */
@SuppressWarnings("deprecation")
@Deprecated
@Override
public PlaybackParameters getPlaybackParameters() {
return new PlaybackParameters(playbackInfo.playbackSpeed);
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {
checkState(playbackSpeed > 0);
if (playbackInfo.playbackSpeed == playbackSpeed) {
if (playbackParameters == null) {
playbackParameters = PlaybackParameters.DEFAULT;
}
if (playbackInfo.playbackParameters.equals(playbackParameters)) {
return;
}
PlaybackInfo newPlaybackInfo = playbackInfo.copyWithPlaybackSpeed(playbackSpeed);
PlaybackInfo newPlaybackInfo = playbackInfo.copyWithPlaybackParameters(playbackParameters);
pendingOperationAcks++;
internalPlayer.setPlaybackSpeed(playbackSpeed);
internalPlayer.setPlaybackParameters(playbackParameters);
updatePlaybackInfo(
newPlaybackInfo,
/* positionDiscontinuity= */ false,
......@@ -655,8 +640,8 @@ import java.util.concurrent.TimeoutException;
}
@Override
public float getPlaybackSpeed() {
return playbackInfo.playbackSpeed;
public PlaybackParameters getPlaybackParameters() {
return playbackInfo.playbackParameters;
}
@Override
......@@ -1366,7 +1351,7 @@ import java.util.concurrent.TimeoutException;
private final boolean playWhenReadyChanged;
private final boolean playbackSuppressionReasonChanged;
private final boolean isPlayingChanged;
private final boolean playbackSpeedChanged;
private final boolean playbackParametersChanged;
private final boolean offloadSchedulingEnabledChanged;
public PlaybackInfoUpdate(
......@@ -1405,7 +1390,8 @@ import java.util.concurrent.TimeoutException;
playbackSuppressionReasonChanged =
previousPlaybackInfo.playbackSuppressionReason != playbackInfo.playbackSuppressionReason;
isPlayingChanged = isPlaying(previousPlaybackInfo) != isPlaying(playbackInfo);
playbackSpeedChanged = previousPlaybackInfo.playbackSpeed != playbackInfo.playbackSpeed;
playbackParametersChanged =
!previousPlaybackInfo.playbackParameters.equals(playbackInfo.playbackParameters);
offloadSchedulingEnabledChanged =
previousPlaybackInfo.offloadSchedulingEnabled != playbackInfo.offloadSchedulingEnabled;
}
......@@ -1473,13 +1459,11 @@ import java.util.concurrent.TimeoutException;
invokeAll(
listenerSnapshot, listener -> listener.onIsPlayingChanged(isPlaying(playbackInfo)));
}
if (playbackSpeedChanged) {
PlaybackParameters playbackParameters = new PlaybackParameters(playbackInfo.playbackSpeed);
if (playbackParametersChanged) {
invokeAll(
listenerSnapshot,
listener -> {
listener.onPlaybackSpeedChanged(playbackInfo.playbackSpeed);
listener.onPlaybackParametersChanged(playbackParameters);
listener.onPlaybackParametersChanged(playbackInfo.playbackParameters);
});
}
if (seekProcessed) {
......
......@@ -27,7 +27,7 @@ import android.os.SystemClock;
import android.util.Pair;
import androidx.annotation.CheckResult;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.DefaultMediaClock.PlaybackSpeedListener;
import com.google.android.exoplayer2.DefaultMediaClock.PlaybackParametersListener;
import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Player.PlayWhenReadyChangeReason;
import com.google.android.exoplayer2.Player.PlaybackSuppressionReason;
......@@ -61,7 +61,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
MediaPeriod.Callback,
TrackSelector.InvalidationListener,
MediaSourceList.MediaSourceListInfoRefreshListener,
PlaybackSpeedListener,
PlaybackParametersListener,
PlayerMessage.Sender {
private static final String TAG = "ExoPlayerImplInternal";
......@@ -121,7 +121,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_SET_PLAY_WHEN_READY = 1;
private static final int MSG_DO_SOME_WORK = 2;
private static final int MSG_SEEK_TO = 3;
private static final int MSG_SET_PLAYBACK_SPEED = 4;
private static final int MSG_SET_PLAYBACK_PARAMETERS = 4;
private static final int MSG_SET_SEEK_PARAMETERS = 5;
private static final int MSG_STOP = 6;
private static final int MSG_RELEASE = 7;
......@@ -133,7 +133,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_SET_FOREGROUND_MODE = 13;
private static final int MSG_SEND_MESSAGE = 14;
private static final int MSG_SEND_MESSAGE_TO_TARGET_THREAD = 15;
private static final int MSG_PLAYBACK_SPEED_CHANGED_INTERNAL = 16;
private static final int MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL = 16;
private static final int MSG_SET_MEDIA_SOURCES = 17;
private static final int MSG_ADD_MEDIA_SOURCES = 18;
private static final int MSG_MOVE_MEDIA_SOURCES = 19;
......@@ -301,8 +301,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
.sendToTarget();
}
public void setPlaybackSpeed(float playbackSpeed) {
handler.obtainMessage(MSG_SET_PLAYBACK_SPEED, playbackSpeed).sendToTarget();
public void setPlaybackParameters(PlaybackParameters playbackParameters) {
handler.obtainMessage(MSG_SET_PLAYBACK_PARAMETERS, playbackParameters).sendToTarget();
}
public void setSeekParameters(SeekParameters seekParameters) {
......@@ -432,11 +432,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
handler.sendEmptyMessage(MSG_TRACK_SELECTION_INVALIDATED);
}
// DefaultMediaClock.PlaybackSpeedListener implementation.
// DefaultMediaClock.PlaybackParametersListener implementation.
@Override
public void onPlaybackSpeedChanged(float playbackSpeed) {
sendPlaybackSpeedChangedInternal(playbackSpeed, /* acknowledgeCommand= */ false);
public void onPlaybackParametersChanged(PlaybackParameters newPlaybackParameters) {
sendPlaybackParametersChangedInternal(newPlaybackParameters, /* acknowledgeCommand= */ false);
}
// Handler.Callback implementation.
......@@ -467,8 +467,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
case MSG_SEEK_TO:
seekToInternal((SeekPosition) msg.obj);
break;
case MSG_SET_PLAYBACK_SPEED:
setPlaybackSpeedInternal((Float) msg.obj);
case MSG_SET_PLAYBACK_PARAMETERS:
setPlaybackParametersInternal((PlaybackParameters) msg.obj);
break;
case MSG_SET_SEEK_PARAMETERS:
setSeekParametersInternal((SeekParameters) msg.obj);
......@@ -489,8 +489,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
case MSG_TRACK_SELECTION_INVALIDATED:
reselectTracksInternal();
break;
case MSG_PLAYBACK_SPEED_CHANGED_INTERNAL:
handlePlaybackSpeed((Float) msg.obj, /* acknowledgeCommand= */ msg.arg1 != 0);
case MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL:
handlePlaybackParameters(
(PlaybackParameters) msg.obj, /* acknowledgeCommand= */ msg.arg1 != 0);
break;
case MSG_SEND_MESSAGE:
sendMessageInternal((PlayerMessage) msg.obj);
......@@ -1184,9 +1185,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
notifyTrackSelectionDiscontinuity();
}
private void setPlaybackSpeedInternal(float playbackSpeed) {
mediaClock.setPlaybackSpeed(playbackSpeed);
sendPlaybackSpeedChangedInternal(mediaClock.getPlaybackSpeed(), /* acknowledgeCommand= */ true);
private void setPlaybackParametersInternal(PlaybackParameters playbackParameters) {
mediaClock.setPlaybackParameters(playbackParameters);
sendPlaybackParametersChangedInternal(
mediaClock.getPlaybackParameters(), /* acknowledgeCommand= */ true);
}
private void setSeekParametersInternal(SeekParameters seekParameters) {
......@@ -1303,7 +1305,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
mediaPeriodId,
playbackInfo.playWhenReady,
playbackInfo.playbackSuppressionReason,
playbackInfo.playbackSpeed,
playbackInfo.playbackParameters,
startPositionUs,
/* totalBufferedDurationUs= */ 0,
startPositionUs,
......@@ -1508,7 +1510,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
private void reselectTracksInternal() throws ExoPlaybackException {
float playbackSpeed = mediaClock.getPlaybackSpeed();
float playbackSpeed = mediaClock.getPlaybackParameters().speed;
// Reselect tracks on each period in turn, until the selection changes.
MediaPeriodHolder periodHolder = queue.getPlayingPeriod();
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
......@@ -1626,7 +1628,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
boolean bufferedToEnd = loadingHolder.isFullyBuffered() && loadingHolder.info.isFinal;
return bufferedToEnd
|| loadControl.shouldStartPlayback(
getTotalBufferedDurationUs(), mediaClock.getPlaybackSpeed(), rebuffering);
getTotalBufferedDurationUs(), mediaClock.getPlaybackParameters().speed, rebuffering);
}
private boolean isTimelineReady() {
......@@ -1962,7 +1964,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
return;
}
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
loadingPeriodHolder.handlePrepared(mediaClock.getPlaybackSpeed(), playbackInfo.timeline);
loadingPeriodHolder.handlePrepared(
mediaClock.getPlaybackParameters().speed, playbackInfo.timeline);
updateLoadControlTrackSelection(
loadingPeriodHolder.getTrackGroups(), loadingPeriodHolder.getTrackSelectorResult());
if (loadingPeriodHolder == queue.getPlayingPeriod()) {
......@@ -1987,14 +1990,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
maybeContinueLoading();
}
private void handlePlaybackSpeed(float playbackSpeed, boolean acknowledgeCommand)
private void handlePlaybackParameters(
PlaybackParameters playbackParameters, boolean acknowledgeCommand)
throws ExoPlaybackException {
playbackInfoUpdate.incrementPendingOperationAcks(acknowledgeCommand ? 1 : 0);
playbackInfo = playbackInfo.copyWithPlaybackSpeed(playbackSpeed);
updateTrackSelectionPlaybackSpeed(playbackSpeed);
playbackInfo = playbackInfo.copyWithPlaybackParameters(playbackParameters);
updateTrackSelectionPlaybackSpeed(playbackParameters.speed);
for (Renderer renderer : renderers) {
if (renderer != null) {
renderer.setOperatingRate(playbackSpeed);
renderer.setOperatingRate(playbackParameters.speed);
}
}
}
......@@ -2020,7 +2024,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
: loadingPeriodHolder.toPeriodTime(rendererPositionUs)
- loadingPeriodHolder.info.startPositionUs;
return loadControl.shouldContinueLoading(
playbackPositionUs, bufferedDurationUs, mediaClock.getPlaybackSpeed());
playbackPositionUs, bufferedDurationUs, mediaClock.getPlaybackParameters().speed);
}
private boolean isLoadingPossible() {
......@@ -2196,10 +2200,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
loadControl.onTracksSelected(renderers, trackGroups, trackSelectorResult.selections);
}
private void sendPlaybackSpeedChangedInternal(float playbackSpeed, boolean acknowledgeCommand) {
private void sendPlaybackParametersChangedInternal(
PlaybackParameters playbackParameters, boolean acknowledgeCommand) {
handler
.obtainMessage(
MSG_PLAYBACK_SPEED_CHANGED_INTERNAL, acknowledgeCommand ? 1 : 0, 0, playbackSpeed)
MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL,
acknowledgeCommand ? 1 : 0,
0,
playbackParameters)
.sendToTarget();
}
......
......@@ -63,8 +63,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
public final boolean playWhenReady;
/** Reason why playback is suppressed even though {@link #playWhenReady} is {@code true}. */
@PlaybackSuppressionReason public final int playbackSuppressionReason;
/** The playback speed. */
public final float playbackSpeed;
/** The playback parameters. */
public final PlaybackParameters playbackParameters;
/** Whether offload scheduling is enabled for the main player loop. */
public final boolean offloadSchedulingEnabled;
......@@ -105,7 +105,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
PLACEHOLDER_MEDIA_PERIOD_ID,
/* playWhenReady= */ false,
Player.PLAYBACK_SUPPRESSION_REASON_NONE,
Player.DEFAULT_PLAYBACK_SPEED,
PlaybackParameters.DEFAULT,
/* bufferedPositionUs= */ 0,
/* totalBufferedDurationUs= */ 0,
/* positionUs= */ 0,
......@@ -119,10 +119,14 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
* @param periodId See {@link #periodId}.
* @param requestedContentPositionUs See {@link #requestedContentPositionUs}.
* @param playbackState See {@link #playbackState}.
* @param playbackError See {@link #playbackError}.
* @param isLoading See {@link #isLoading}.
* @param trackGroups See {@link #trackGroups}.
* @param trackSelectorResult See {@link #trackSelectorResult}.
* @param loadingMediaPeriodId See {@link #loadingMediaPeriodId}.
* @param playWhenReady See {@link #playWhenReady}.
* @param playbackSuppressionReason See {@link #playbackSuppressionReason}.
* @param playbackParameters See {@link #playbackParameters}.
* @param bufferedPositionUs See {@link #bufferedPositionUs}.
* @param totalBufferedDurationUs See {@link #totalBufferedDurationUs}.
* @param positionUs See {@link #positionUs}.
......@@ -140,7 +144,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
MediaPeriodId loadingMediaPeriodId,
boolean playWhenReady,
@PlaybackSuppressionReason int playbackSuppressionReason,
float playbackSpeed,
PlaybackParameters playbackParameters,
long bufferedPositionUs,
long totalBufferedDurationUs,
long positionUs,
......@@ -156,7 +160,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
this.loadingMediaPeriodId = loadingMediaPeriodId;
this.playWhenReady = playWhenReady;
this.playbackSuppressionReason = playbackSuppressionReason;
this.playbackSpeed = playbackSpeed;
this.playbackParameters = playbackParameters;
this.bufferedPositionUs = bufferedPositionUs;
this.totalBufferedDurationUs = totalBufferedDurationUs;
this.positionUs = positionUs;
......@@ -201,7 +205,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
playbackSpeed,
playbackParameters,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs,
......@@ -228,7 +232,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
playbackSpeed,
playbackParameters,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs,
......@@ -255,7 +259,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
playbackSpeed,
playbackParameters,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs,
......@@ -282,7 +286,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
playbackSpeed,
playbackParameters,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs,
......@@ -309,7 +313,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
playbackSpeed,
playbackParameters,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs,
......@@ -336,7 +340,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
playbackSpeed,
playbackParameters,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs,
......@@ -367,7 +371,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
playbackSpeed,
playbackParameters,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs,
......@@ -375,13 +379,13 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
}
/**
* Copies playback info with new playback speed.
* Copies playback info with new playback parameters.
*
* @param playbackSpeed New playback speed. See {@link #playbackSpeed}.
* @return Copied playback info with new playback speed.
* @param playbackParameters New playback parameters. See {@link #playbackParameters}.
* @return Copied playback info with new playback parameters.
*/
@CheckResult
public PlaybackInfo copyWithPlaybackSpeed(float playbackSpeed) {
public PlaybackInfo copyWithPlaybackParameters(PlaybackParameters playbackParameters) {
return new PlaybackInfo(
timeline,
periodId,
......@@ -394,7 +398,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
playbackSpeed,
playbackParameters,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs,
......@@ -422,7 +426,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
playbackSpeed,
playbackParameters,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs,
......
......@@ -17,13 +17,9 @@ package com.google.android.exoplayer2;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
/**
* @deprecated Use {@link Player#setPlaybackSpeed(float)} and {@link
* Player.AudioComponent#setSkipSilenceEnabled(boolean)} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
/** Parameters that apply to playback, including speed setting. */
public final class PlaybackParameters {
/** The default playback parameters: real-time playback with no silence skipping. */
......@@ -32,16 +28,34 @@ public final class PlaybackParameters {
/** The factor by which playback will be sped up. */
public final float speed;
/** The factor by which pitch will be shifted. */
public final float pitch;
private final int scaledUsPerMs;
/**
* Creates new playback parameters that set the playback speed.
* Creates new playback parameters that set the playback speed. The pitch of audio will not be
* adjusted, so the effect is to time-stretch the audio.
*
* @param speed The factor by which playback will be sped up. Must be greater than zero.
*/
public PlaybackParameters(float speed) {
this(speed, /* pitch= */ 1f);
}
/**
* Creates new playback parameters that set the playback speed/pitch.
*
* @param speed The factor by which playback will be sped up. Must be greater than zero.
* @param pitch The factor by which the pitch of audio will be adjusted. Must be greater than
* zero. Useful values are {@code 1} (to time-stretch audio) and the same value as passed in
* as the {@code speed} (to resample audio, which is useful for slow-motion videos).
*/
public PlaybackParameters(float speed, float pitch) {
Assertions.checkArgument(speed > 0);
Assertions.checkArgument(pitch > 0);
this.speed = speed;
this.pitch = pitch;
scaledUsPerMs = Math.round(speed * 1000f);
}
......@@ -65,11 +79,19 @@ public final class PlaybackParameters {
return false;
}
PlaybackParameters other = (PlaybackParameters) obj;
return this.speed == other.speed;
return this.speed == other.speed && this.pitch == other.pitch;
}
@Override
public int hashCode() {
return Float.floatToRawIntBits(speed);
int result = 17;
result = 31 * result + Float.floatToRawIntBits(speed);
result = 31 * result + Float.floatToRawIntBits(pitch);
return result;
}
@Override
public String toString() {
return Util.formatInvariant("PlaybackParameters(speed=%.2f, pitch=%.2f)", speed, pitch);
}
}
......@@ -583,22 +583,16 @@ public interface Player {
default void onPositionDiscontinuity(@DiscontinuityReason int reason) {}
/**
* @deprecated Use {@link #onPlaybackSpeedChanged(float)} and {@link
* AudioListener#onSkipSilenceEnabledChanged(boolean)} instead.
* Called when the current playback parameters change. The playback parameters may change due to
* a call to {@link #setPlaybackParameters(PlaybackParameters)}, or the player itself may change
* them (for example, if audio playback switches to passthrough or offload mode, where speed
* adjustment is no longer possible).
*
* @param playbackParameters The playback parameters.
*/
@SuppressWarnings("deprecation")
@Deprecated
default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {}
/**
* Called when the current playback speed changes. The normal playback speed is 1. The speed may
* change due to a call to {@link #setPlaybackSpeed(float)}, or the player itself may change it
* (for example, if audio playback switches to passthrough mode, where speed adjustment is no
* longer possible).
*/
default void onPlaybackSpeedChanged(float playbackSpeed) {}
/**
* @deprecated Seeks are processed without delay. Listen to {@link
* #onPositionDiscontinuity(int)} with reason {@link #DISCONTINUITY_REASON_SEEK} instead.
*/
......@@ -810,9 +804,6 @@ public interface Player {
*/
int MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED = 3;
/** The default playback speed. */
float DEFAULT_PLAYBACK_SPEED = 1.0f;
/** Returns the component of this player for audio output, or null if audio is not supported. */
@Nullable
AudioComponent getAudioComponent();
......@@ -1161,38 +1152,23 @@ public interface Player {
void next();
/**
* @deprecated Use {@link #setPlaybackSpeed(float)} or {@link
* AudioComponent#setSkipSilenceEnabled(boolean)} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
void setPlaybackParameters(@Nullable PlaybackParameters playbackParameters);
/**
* @deprecated Use {@link #getPlaybackSpeed()} or {@link AudioComponent#getSkipSilenceEnabled()}
* instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
PlaybackParameters getPlaybackParameters();
/**
* Attempts to set the playback speed.
* Attempts to set the playback parameters. Passing {@code null} sets the parameters to the
* default, {@link PlaybackParameters#DEFAULT}, which means there is no speed or pitch adjustment.
*
* <p>Playback speed changes may cause the player to buffer. {@link
* EventListener#onPlaybackSpeedChanged(float)} will be called whenever the currently active
* playback speed change.
* <p>Playback parameters changes may cause the player to buffer. {@link
* EventListener#onPlaybackParametersChanged(PlaybackParameters)} will be called whenever the
* currently active playback parameters change.
*
* @param playbackSpeed The playback speed.
* @param playbackParameters The playback parameters, or {@code null} to use the defaults.
*/
void setPlaybackSpeed(float playbackSpeed);
void setPlaybackParameters(@Nullable PlaybackParameters playbackParameters);
/**
* Returns the currently active playback speed.
* Returns the currently active playback parameters.
*
* @see EventListener#onPlaybackSpeedChanged(float)
* @see EventListener#onPlaybackParametersChanged(PlaybackParameters)
*/
float getPlaybackSpeed();
PlaybackParameters getPlaybackParameters();
/**
* Stops playback without resetting the player. Use {@link #pause()} rather than this method if
......
......@@ -1030,18 +1030,23 @@ public class SimpleExoPlayer extends BasePlayer
this.priorityTaskManager = priorityTaskManager;
}
/** @deprecated Use {@link #setPlaybackSpeed(float)} instead. */
/**
* Sets the {@link PlaybackParams} governing audio playback.
*
* @param params The {@link PlaybackParams}, or null to clear any previously set parameters.
* @deprecated Use {@link #setPlaybackParameters(PlaybackParameters)}.
*/
@Deprecated
@RequiresApi(23)
public void setPlaybackParams(@Nullable PlaybackParams params) {
float playbackSpeed;
PlaybackParameters playbackParameters;
if (params != null) {
params.allowDefaults();
playbackSpeed = params.getSpeed();
playbackParameters = new PlaybackParameters(params.getSpeed(), params.getPitch());
} else {
playbackSpeed = 1.0f;
playbackParameters = null;
}
setPlaybackSpeed(playbackSpeed);
setPlaybackParameters(playbackParameters);
}
/** Returns the video format currently being played, or null if no video is being played. */
......@@ -1623,21 +1628,12 @@ public class SimpleExoPlayer extends BasePlayer
player.seekTo(windowIndex, positionMs);
}
/**
* @deprecated Use {@link #setPlaybackSpeed(float)} and {@link #setSkipSilenceEnabled(boolean)}
* instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
@Override
public void setPlaybackParameters(@Nullable PlaybackParameters playbackParameters) {
verifyApplicationThread();
player.setPlaybackParameters(playbackParameters);
}
/** @deprecated Use {@link #getPlaybackSpeed()} and {@link #getSkipSilenceEnabled()} instead. */
@SuppressWarnings("deprecation")
@Deprecated
@Override
public PlaybackParameters getPlaybackParameters() {
verifyApplicationThread();
......@@ -1645,18 +1641,6 @@ public class SimpleExoPlayer extends BasePlayer
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {
verifyApplicationThread();
player.setPlaybackSpeed(playbackSpeed);
}
@Override
public float getPlaybackSpeed() {
verifyApplicationThread();
return player.getPlaybackSpeed();
}
@Override
public void setSeekParameters(@Nullable SeekParameters seekParameters) {
verifyApplicationThread();
player.setSeekParameters(seekParameters);
......
......@@ -552,12 +552,6 @@ public class AnalyticsCollector
}
}
/**
* @deprecated Use {@link #onPlaybackSpeedChanged(float)} and {@link
* #onSkipSilenceEnabledChanged(boolean)} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
@Override
public final void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
......@@ -566,14 +560,6 @@ public class AnalyticsCollector
}
}
@Override
public void onPlaybackSpeedChanged(float playbackSpeed) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onPlaybackSpeedChanged(eventTime, playbackSpeed);
}
}
@SuppressWarnings("deprecation")
@Override
public final void onSeekProcessed() {
......
......@@ -279,21 +279,13 @@ public interface AnalyticsListener {
default void onSeekProcessed(EventTime eventTime) {}
/**
* @deprecated Use {@link #onPlaybackSpeedChanged(EventTime, float)} and {@link
* #onSkipSilenceEnabledChanged(EventTime, boolean)} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
default void onPlaybackParametersChanged(
EventTime eventTime, PlaybackParameters playbackParameters) {}
/**
* Called when the playback speed changes.
* Called when the playback parameters changed.
*
* @param eventTime The event time.
* @param playbackSpeed The playback speed.
* @param playbackParameters The new playback parameters.
*/
default void onPlaybackSpeedChanged(EventTime eventTime, float playbackSpeed) {}
default void onPlaybackParametersChanged(
EventTime eventTime, PlaybackParameters playbackParameters) {}
/**
* Called when the repeat mode changed.
......
......@@ -22,6 +22,7 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
......@@ -334,8 +335,9 @@ public final class PlaybackStatsListener
}
@Override
public void onPlaybackSpeedChanged(EventTime eventTime, float playbackSpeed) {
this.playbackSpeed = playbackSpeed;
public void onPlaybackParametersChanged(
EventTime eventTime, PlaybackParameters playbackParameters) {
playbackSpeed = playbackParameters.speed;
maybeAddSession(eventTime);
for (PlaybackStatsTracker tracker : playbackStatsTrackers.values()) {
tracker.onPlaybackSpeedChanged(eventTime, playbackSpeed);
......
......@@ -20,6 +20,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
......@@ -306,16 +307,21 @@ public interface AudioSink {
*/
boolean hasPendingData();
/** Sets the playback speed. */
void setPlaybackSpeed(float playbackSpeed);
/**
* Attempts to set the playback parameters. The audio sink may override these parameters if they
* are not supported.
*
* @param playbackParameters The new playback parameters to attempt to set.
*/
void setPlaybackParameters(PlaybackParameters playbackParameters);
/** Gets the playback speed. */
float getPlaybackSpeed();
/** Returns the active {@link PlaybackParameters}. */
PlaybackParameters getPlaybackParameters();
/** Sets whether silences should be skipped in the audio stream. */
void setSkipSilenceEnabled(boolean skipSilenceEnabled);
/** Gets whether silences are skipped in the audio stream. */
/** Returns whether silences are skipped in the audio stream. */
boolean getSkipSilenceEnabled();
/**
......
......@@ -29,6 +29,7 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
......@@ -498,13 +499,13 @@ public abstract class DecoderAudioRenderer<
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {
audioSink.setPlaybackSpeed(playbackSpeed);
public void setPlaybackParameters(PlaybackParameters playbackParameters) {
audioSink.setPlaybackParameters(playbackParameters);
}
@Override
public float getPlaybackSpeed() {
return audioSink.getPlaybackSpeed();
public PlaybackParameters getPlaybackParameters() {
return audioSink.getPlaybackParameters();
}
@Override
......
......@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.audio;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import java.nio.ByteBuffer;
/** An overridable {@link AudioSink} implementation forwarding all methods to another sink. */
......@@ -88,13 +89,13 @@ public class ForwardingAudioSink implements AudioSink {
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {
sink.setPlaybackSpeed(playbackSpeed);
public void setPlaybackParameters(PlaybackParameters playbackParameters) {
sink.setPlaybackParameters(playbackParameters);
}
@Override
public float getPlaybackSpeed() {
return sink.getPlaybackSpeed();
public PlaybackParameters getPlaybackParameters() {
return sink.getPlaybackParameters();
}
@Override
......
......@@ -33,6 +33,7 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
......@@ -545,13 +546,13 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {
audioSink.setPlaybackSpeed(playbackSpeed);
public void setPlaybackParameters(PlaybackParameters playbackParameters) {
audioSink.setPlaybackParameters(playbackParameters);
}
@Override
public float getPlaybackSpeed() {
return audioSink.getPlaybackSpeed();
public PlaybackParameters getPlaybackParameters() {
return audioSink.getPlaybackParameters();
}
@Override
......
......@@ -37,6 +37,7 @@ import java.util.Arrays;
private final int inputSampleRateHz;
private final int channelCount;
private final float speed;
private final float pitch;
private final float rate;
private final int minPeriod;
private final int maxPeriod;
......@@ -63,12 +64,15 @@ import java.util.Arrays;
* @param inputSampleRateHz The sample rate of input audio, in hertz.
* @param channelCount The number of channels in the input audio.
* @param speed The speedup factor for output audio.
* @param pitch The pitch factor for output audio.
* @param outputSampleRateHz The sample rate for output audio, in hertz.
*/
public Sonic(int inputSampleRateHz, int channelCount, float speed, int outputSampleRateHz) {
public Sonic(
int inputSampleRateHz, int channelCount, float speed, float pitch, int outputSampleRateHz) {
this.inputSampleRateHz = inputSampleRateHz;
this.channelCount = channelCount;
this.speed = speed;
this.pitch = pitch;
rate = (float) inputSampleRateHz / outputSampleRateHz;
minPeriod = inputSampleRateHz / MAXIMUM_PITCH;
maxPeriod = inputSampleRateHz / MINIMUM_PITCH;
......@@ -118,8 +122,10 @@ import java.util.Arrays;
*/
public void queueEndOfStream() {
int remainingFrameCount = inputFrameCount;
float s = speed / pitch;
float r = rate * pitch;
int expectedOutputFrames =
outputFrameCount + (int) ((remainingFrameCount / speed + pitchFrameCount) / rate + 0.5f);
outputFrameCount + (int) ((remainingFrameCount / s + pitchFrameCount) / r + 0.5f);
// Add enough silence to flush both input and pitch buffers.
inputBuffer =
......@@ -464,14 +470,16 @@ import java.util.Arrays;
private void processStreamInput() {
// Resample as many pitch periods as we have buffered on the input.
int originalOutputFrameCount = outputFrameCount;
if (speed > 1.00001 || speed < 0.99999) {
changeSpeed(speed);
float s = speed / pitch;
float r = rate * pitch;
if (s > 1.00001 || s < 0.99999) {
changeSpeed(s);
} else {
copyToOutput(inputBuffer, 0, inputFrameCount);
inputFrameCount = 0;
}
if (rate != 1.0f) {
adjustRate(rate, originalOutputFrameCount);
if (r != 1.0f) {
adjustRate(r, originalOutputFrameCount);
}
}
......
......@@ -43,6 +43,7 @@ public final class SonicAudioProcessor implements AudioProcessor {
private int pendingOutputSampleRate;
private float speed;
private float pitch;
private AudioFormat pendingInputAudioFormat;
private AudioFormat pendingOutputAudioFormat;
......@@ -61,6 +62,7 @@ public final class SonicAudioProcessor implements AudioProcessor {
/** Creates a new Sonic audio processor. */
public SonicAudioProcessor() {
speed = 1f;
pitch = 1f;
pendingInputAudioFormat = AudioFormat.NOT_SET;
pendingOutputAudioFormat = AudioFormat.NOT_SET;
inputAudioFormat = AudioFormat.NOT_SET;
......@@ -88,6 +90,22 @@ public final class SonicAudioProcessor implements AudioProcessor {
}
/**
* Sets the playback pitch. This method may only be called after draining data through the
* processor. The value returned by {@link #isActive()} may change, and the processor must be
* {@link #flush() flushed} before queueing more data.
*
* @param pitch The requested new pitch.
* @return The actual new pitch.
*/
public float setPitch(float pitch) {
if (this.pitch != pitch) {
this.pitch = pitch;
pendingSonicRecreation = true;
}
return pitch;
}
/**
* Sets the sample rate for output audio, in Hertz. Pass {@link #SAMPLE_RATE_NO_CHANGE} to output
* audio at the same sample rate as the input. After calling this method, call {@link
* #configure(AudioFormat)} to configure the processor with the new sample rate.
......@@ -140,6 +158,7 @@ public final class SonicAudioProcessor implements AudioProcessor {
public boolean isActive() {
return pendingOutputAudioFormat.sampleRate != Format.NO_VALUE
&& (Math.abs(speed - 1f) >= CLOSE_THRESHOLD
|| Math.abs(pitch - 1f) >= CLOSE_THRESHOLD
|| pendingOutputAudioFormat.sampleRate != pendingInputAudioFormat.sampleRate);
}
......@@ -200,6 +219,7 @@ public final class SonicAudioProcessor implements AudioProcessor {
inputAudioFormat.sampleRate,
inputAudioFormat.channelCount,
speed,
pitch,
outputAudioFormat.sampleRate);
} else if (sonic != null) {
sonic.flush();
......@@ -214,6 +234,7 @@ public final class SonicAudioProcessor implements AudioProcessor {
@Override
public void reset() {
speed = 1f;
pitch = 1f;
pendingInputAudioFormat = AudioFormat.NOT_SET;
pendingOutputAudioFormat = AudioFormat.NOT_SET;
inputAudioFormat = AudioFormat.NOT_SET;
......
......@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.PlaybackSuppressionReason;
import com.google.android.exoplayer2.RendererCapabilities;
......@@ -147,8 +148,9 @@ public class EventLogger implements AnalyticsListener {
}
@Override
public void onPlaybackSpeedChanged(EventTime eventTime, float playbackSpeed) {
logd(eventTime, "playbackSpeed", Float.toString(playbackSpeed));
public void onPlaybackParametersChanged(
EventTime eventTime, PlaybackParameters playbackParameters) {
logd(eventTime, "playbackParameters", playbackParameters.toString());
}
@Override
......
......@@ -15,6 +15,8 @@
*/
package com.google.android.exoplayer2.util;
import com.google.android.exoplayer2.PlaybackParameters;
/**
* Tracks the progression of media time.
*/
......@@ -26,13 +28,13 @@ public interface MediaClock {
long getPositionUs();
/**
* Attempts to set the playback speed. The media clock may override the speed if changing the
* speed is not supported.
* Attempts to set the playback parameters. The media clock may override the speed if changing the
* playback parameters is not supported.
*
* @param playbackSpeed The playback speed to attempt to set.
* @param playbackParameters The playback parameters to attempt to set.
*/
void setPlaybackSpeed(float playbackSpeed);
void setPlaybackParameters(PlaybackParameters playbackParameters);
/** Returns the active playback speed. */
float getPlaybackSpeed();
/** Returns the active playback parameters. */
PlaybackParameters getPlaybackParameters();
}
......@@ -16,7 +16,7 @@
package com.google.android.exoplayer2.util;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.PlaybackParameters;
/**
* A {@link MediaClock} whose position advances with real time based on the playback parameters when
......@@ -29,8 +29,7 @@ public final class StandaloneMediaClock implements MediaClock {
private boolean started;
private long baseUs;
private long baseElapsedMs;
private float playbackSpeed;
private int scaledUsPerMs;
private PlaybackParameters playbackParameters;
/**
* Creates a new standalone media clock using the given {@link Clock} implementation.
......@@ -39,8 +38,7 @@ public final class StandaloneMediaClock implements MediaClock {
*/
public StandaloneMediaClock(Clock clock) {
this.clock = clock;
playbackSpeed = Player.DEFAULT_PLAYBACK_SPEED;
scaledUsPerMs = getScaledUsPerMs(playbackSpeed);
playbackParameters = PlaybackParameters.DEFAULT;
}
/**
......@@ -80,33 +78,29 @@ public final class StandaloneMediaClock implements MediaClock {
long positionUs = baseUs;
if (started) {
long elapsedSinceBaseMs = clock.elapsedRealtime() - baseElapsedMs;
if (playbackSpeed == 1f) {
if (playbackParameters.speed == 1f) {
positionUs += C.msToUs(elapsedSinceBaseMs);
} else {
// Add the media time in microseconds that will elapse in elapsedSinceBaseMs milliseconds of
// wallclock time
positionUs += elapsedSinceBaseMs * scaledUsPerMs;
positionUs += playbackParameters.getMediaTimeUsForPlayoutTimeMs(elapsedSinceBaseMs);
}
}
return positionUs;
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {
public void setPlaybackParameters(PlaybackParameters playbackParameters) {
// Store the current position as the new base, in case the playback speed has changed.
if (started) {
resetPosition(getPositionUs());
}
this.playbackSpeed = playbackSpeed;
scaledUsPerMs = getScaledUsPerMs(playbackSpeed);
this.playbackParameters = playbackParameters;
}
@Override
public float getPlaybackSpeed() {
return playbackSpeed;
public PlaybackParameters getPlaybackParameters() {
return playbackParameters;
}
private static int getScaledUsPerMs(float playbackSpeed) {
return Math.round(playbackSpeed * 1000f);
}
}
......@@ -328,11 +328,11 @@ public final class ExoPlayerTest {
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {}
public void setPlaybackParameters(PlaybackParameters playbackParameters) {}
@Override
public float getPlaybackSpeed() {
return Player.DEFAULT_PLAYBACK_SPEED;
public PlaybackParameters getPlaybackParameters() {
return PlaybackParameters.DEFAULT;
}
@Override
......@@ -1010,7 +1010,7 @@ public final class ExoPlayerTest {
}
})
// Set playback speed (while the fake media period is not yet prepared).
.setPlaybackSpeed(2f)
.setPlaybackParameters(new PlaybackParameters(/* speed= */ 2f))
// Complete preparation of the fake media period.
.executeRunnable(() -> fakeMediaPeriodHolder[0].setPreparationComplete())
.build();
......@@ -3378,18 +3378,18 @@ public final class ExoPlayerTest {
SimpleExoPlayer player,
DefaultTrackSelector trackSelector,
@Nullable Surface surface) {
maskedPlaybackSpeeds.add(player.getPlaybackSpeed());
maskedPlaybackSpeeds.add(player.getPlaybackParameters().speed);
}
};
ActionSchedule actionSchedule =
new ActionSchedule.Builder(TAG)
.pause()
.waitForPlaybackState(Player.STATE_READY)
.setPlaybackSpeed(1.1f)
.setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.1f))
.apply(getPlaybackSpeedAction)
.setPlaybackSpeed(1.2f)
.setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.2f))
.apply(getPlaybackSpeedAction)
.setPlaybackSpeed(1.3f)
.setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.3f))
.apply(getPlaybackSpeedAction)
.play()
.build();
......@@ -3397,8 +3397,8 @@ public final class ExoPlayerTest {
EventListener listener =
new EventListener() {
@Override
public void onPlaybackSpeedChanged(float playbackSpeed) {
reportedPlaybackSpeeds.add(playbackSpeed);
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
reportedPlaybackSpeeds.add(playbackParameters.speed);
}
};
new ExoPlayerTestRunner.Builder(context)
......@@ -3424,28 +3424,28 @@ public final class ExoPlayerTest {
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {}
public void setPlaybackParameters(PlaybackParameters playbackParameters) {}
@Override
public float getPlaybackSpeed() {
return Player.DEFAULT_PLAYBACK_SPEED;
public PlaybackParameters getPlaybackParameters() {
return PlaybackParameters.DEFAULT;
}
};
ActionSchedule actionSchedule =
new ActionSchedule.Builder(TAG)
.pause()
.waitForPlaybackState(Player.STATE_READY)
.setPlaybackSpeed(1.1f)
.setPlaybackSpeed(1.2f)
.setPlaybackSpeed(1.3f)
.setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.1f))
.setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.2f))
.setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.3f))
.play()
.build();
List<Float> reportedPlaybackParameters = new ArrayList<>();
List<PlaybackParameters> reportedPlaybackParameters = new ArrayList<>();
EventListener listener =
new EventListener() {
@Override
public void onPlaybackSpeedChanged(float playbackSpeed) {
reportedPlaybackParameters.add(playbackSpeed);
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
reportedPlaybackParameters.add(playbackParameters);
}
};
new ExoPlayerTestRunner.Builder(context)
......@@ -3458,7 +3458,11 @@ public final class ExoPlayerTest {
.blockUntilEnded(TIMEOUT_MS);
assertThat(reportedPlaybackParameters)
.containsExactly(1.1f, 1.2f, 1.3f, Player.DEFAULT_PLAYBACK_SPEED)
.containsExactly(
new PlaybackParameters(/* speed= */ 1.1f),
new PlaybackParameters(/* speed= */ 1.2f),
new PlaybackParameters(/* speed= */ 1.3f),
PlaybackParameters.DEFAULT)
.inOrder();
}
......
......@@ -435,7 +435,7 @@ public final class MediaPeriodQueueTest {
/* loadingMediaPeriodId= */ null,
/* playWhenReady= */ false,
Player.PLAYBACK_SUPPRESSION_REASON_NONE,
/* playbackSpeed= */ Player.DEFAULT_PLAYBACK_SPEED,
/* playbackParameters= */ PlaybackParameters.DEFAULT,
/* bufferedPositionUs= */ 0,
/* totalBufferedDurationUs= */ 0,
/* positionUs= */ 0,
......
......@@ -26,6 +26,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.RenderersFactory;
......@@ -82,7 +83,7 @@ public final class AnalyticsCollectorTest {
private static final int EVENT_POSITION_DISCONTINUITY = 2;
private static final int EVENT_SEEK_STARTED = 3;
private static final int EVENT_SEEK_PROCESSED = 4;
private static final int EVENT_PLAYBACK_SPEED_CHANGED = 5;
private static final int EVENT_PLAYBACK_PARAMETERS_CHANGED = 5;
private static final int EVENT_REPEAT_MODE_CHANGED = 6;
private static final int EVENT_SHUFFLE_MODE_CHANGED = 7;
private static final int EVENT_LOADING_CHANGED = 8;
......@@ -1795,8 +1796,9 @@ public final class AnalyticsCollectorTest {
}
@Override
public void onPlaybackSpeedChanged(EventTime eventTime, float playbackSpeed) {
reportedEvents.add(new ReportedEvent(EVENT_PLAYBACK_SPEED_CHANGED, eventTime));
public void onPlaybackParametersChanged(
EventTime eventTime, PlaybackParameters playbackParameters) {
reportedEvents.add(new ReportedEvent(EVENT_PLAYBACK_PARAMETERS_CHANGED, eventTime));
}
@Override
......
......@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
import android.os.SystemClock;
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource;
......@@ -75,8 +76,8 @@ public final class PlaybackStatsListenerTest {
playbackStatsListener.onPositionDiscontinuity(
EMPTY_TIMELINE_EVENT_TIME, Player.DISCONTINUITY_REASON_SEEK);
playbackStatsListener.onPlaybackSpeedChanged(
EMPTY_TIMELINE_EVENT_TIME, /* playbackSpeed= */ 2.0f);
playbackStatsListener.onPlaybackParametersChanged(
EMPTY_TIMELINE_EVENT_TIME, new PlaybackParameters(/* speed= */ 2.0f));
playbackStatsListener.onPlayWhenReadyChanged(
EMPTY_TIMELINE_EVENT_TIME,
/* playWhenReady= */ true,
......
......@@ -25,6 +25,7 @@ import static org.robolectric.annotation.Config.TARGET_SDK;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.util.MimeTypes;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
......@@ -89,7 +90,7 @@ public final class DefaultAudioSinkTest {
@Test
public void handlesBufferAfterReset_withPlaybackSpeed() throws Exception {
defaultAudioSink.setPlaybackSpeed(/* playbackSpeed= */ 1.5f);
defaultAudioSink.setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.5f));
configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
......@@ -99,7 +100,8 @@ public final class DefaultAudioSinkTest {
configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
assertThat(defaultAudioSink.getPlaybackSpeed()).isEqualTo(1.5f);
assertThat(defaultAudioSink.getPlaybackParameters())
.isEqualTo(new PlaybackParameters(/* speed= */ 1.5f));
}
@Test
......@@ -117,7 +119,7 @@ public final class DefaultAudioSinkTest {
@Test
public void handlesBufferAfterReset_withFormatChangeAndPlaybackSpeed() throws Exception {
defaultAudioSink.setPlaybackSpeed(/* playbackSpeed= */ 1.5f);
defaultAudioSink.setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.5f));
configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
......@@ -127,7 +129,8 @@ public final class DefaultAudioSinkTest {
configureDefaultAudioSink(CHANNEL_COUNT_MONO);
defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
assertThat(defaultAudioSink.getPlaybackSpeed()).isEqualTo(1.5f);
assertThat(defaultAudioSink.getPlaybackParameters())
.isEqualTo(new PlaybackParameters(/* speed= */ 1.5f));
}
@Test
......
......@@ -1085,8 +1085,8 @@ public class PlayerControlView extends FrameLayout {
long mediaTimeUntilNextFullSecondMs = 1000 - position % 1000;
mediaTimeDelayMs = Math.min(mediaTimeDelayMs, mediaTimeUntilNextFullSecondMs);
// Calculate the delay until the next update in real time, taking playbackSpeed into account.
float playbackSpeed = player.getPlaybackSpeed();
// Calculate the delay until the next update in real time, taking playback speed into account.
float playbackSpeed = player.getPlaybackParameters().speed;
long delayMs =
playbackSpeed > 0 ? (long) (mediaTimeDelayMs / playbackSpeed) : MAX_UPDATE_INTERVAL_MS;
......
......@@ -37,6 +37,7 @@ import androidx.media.app.NotificationCompat.MediaStyle;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ControlDispatcher;
import com.google.android.exoplayer2.DefaultControlDispatcher;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
......@@ -923,7 +924,7 @@ public class PlayerNotificationManager {
* <li>The media is not {@link Player#isCurrentWindowDynamic() dynamically changing its
* duration} (like for example a live stream).
* <li>The media is not {@link Player#isPlayingAd() interrupted by an ad}.
* <li>The media is played at {@link Player#getPlaybackSpeed() regular speed}.
* <li>The media is played at {@link Player#getPlaybackParameters() regular speed}.
* <li>The device is running at least API 21 (Lollipop).
* </ul>
*
......@@ -1086,7 +1087,7 @@ public class PlayerNotificationManager {
&& player.isPlaying()
&& !player.isPlayingAd()
&& !player.isCurrentWindowDynamic()
&& player.getPlaybackSpeed() == 1f) {
&& player.getPlaybackParameters().speed == 1f) {
builder
.setWhen(System.currentTimeMillis() - player.getContentPosition())
.setShowWhen(true)
......@@ -1336,7 +1337,7 @@ public class PlayerNotificationManager {
}
@Override
public void onPlaybackSpeedChanged(float playbackSpeed) {
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
postStartOrUpdateNotification();
}
......
......@@ -42,6 +42,7 @@ import com.google.android.exoplayer2.ControlDispatcher;
import com.google.android.exoplayer2.DefaultControlDispatcher;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RendererCapabilities;
......@@ -1408,8 +1409,8 @@ public class StyledPlayerControlView extends FrameLayout {
long mediaTimeUntilNextFullSecondMs = 1000 - position % 1000;
mediaTimeDelayMs = Math.min(mediaTimeDelayMs, mediaTimeUntilNextFullSecondMs);
// Calculate the delay until the next update in real time, taking playbackSpeed into account.
float playbackSpeed = player.getPlaybackSpeed();
// Calculate the delay until the next update in real time, taking playback speed into account.
float playbackSpeed = player.getPlaybackParameters().speed;
long delayMs =
playbackSpeed > 0 ? (long) (mediaTimeDelayMs / playbackSpeed) : MAX_UPDATE_INTERVAL_MS;
......@@ -1425,7 +1426,7 @@ public class StyledPlayerControlView extends FrameLayout {
if (player == null) {
return;
}
float speed = player.getPlaybackSpeed();
float speed = player.getPlaybackParameters().speed;
int currentSpeedMultBy100 = Math.round(speed * 100);
int indexForCurrentSpeed = playbackSpeedMultBy100List.indexOf(currentSpeedMultBy100);
if (indexForCurrentSpeed == UNDEFINED_POSITION) {
......@@ -1481,7 +1482,7 @@ public class StyledPlayerControlView extends FrameLayout {
if (player == null) {
return;
}
player.setPlaybackSpeed(speed);
player.setPlaybackParameters(new PlaybackParameters(speed));
}
/* package */ void requestPlayPauseFocus() {
......@@ -1771,7 +1772,7 @@ public class StyledPlayerControlView extends FrameLayout {
}
@Override
public void onPlaybackSpeedChanged(float playbackSpeed) {
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
updateSettingsPlaybackSpeedLists();
}
......
......@@ -22,6 +22,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.IllegalSeekPositionException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.PlayerMessage;
import com.google.android.exoplayer2.PlayerMessage.Target;
......@@ -608,26 +609,26 @@ public abstract class Action {
}
}
/** Calls {@link Player#setPlaybackSpeed(float)}. */
public static final class SetPlaybackSpeed extends Action {
/** Calls {@link Player#setPlaybackParameters(PlaybackParameters)}. */
public static final class SetPlaybackParameters extends Action {
private final float playbackSpeed;
private final PlaybackParameters playbackParameters;
/**
* Creates a set playback speed action instance.
* Creates a set playback parameters action instance.
*
* @param tag A tag to use for logging.
* @param playbackSpeed The playback speed.
* @param playbackParameters The playback parameters.
*/
public SetPlaybackSpeed(String tag, float playbackSpeed) {
super(tag, "SetPlaybackSpeed:" + playbackSpeed);
this.playbackSpeed = playbackSpeed;
public SetPlaybackParameters(String tag, PlaybackParameters playbackParameters) {
super(tag, "SetPlaybackParameters:" + playbackParameters);
this.playbackParameters = playbackParameters;
}
@Override
protected void doActionImpl(
SimpleExoPlayer player, DefaultTrackSelector trackSelector, @Nullable Surface surface) {
player.setPlaybackSpeed(playbackSpeed);
player.setPlaybackParameters(playbackParameters);
}
}
......
......@@ -20,6 +20,7 @@ import android.view.Surface;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.PlayerMessage;
import com.google.android.exoplayer2.PlayerMessage.Target;
......@@ -35,7 +36,7 @@ import com.google.android.exoplayer2.testutil.Action.Seek;
import com.google.android.exoplayer2.testutil.Action.SendMessages;
import com.google.android.exoplayer2.testutil.Action.SetAudioAttributes;
import com.google.android.exoplayer2.testutil.Action.SetPlayWhenReady;
import com.google.android.exoplayer2.testutil.Action.SetPlaybackSpeed;
import com.google.android.exoplayer2.testutil.Action.SetPlaybackParameters;
import com.google.android.exoplayer2.testutil.Action.SetRendererDisabled;
import com.google.android.exoplayer2.testutil.Action.SetRepeatMode;
import com.google.android.exoplayer2.testutil.Action.SetShuffleModeEnabled;
......@@ -214,14 +215,14 @@ public final class ActionSchedule {
}
/**
* Schedules a playback speed setting action.
* Schedules a playback parameters setting action.
*
* @param playbackSpeed The playback speed to set.
* @param playbackParameters The playback parameters to set.
* @return The builder, for convenience.
* @see Player#setPlaybackSpeed(float)
* @see Player#setPlaybackParameters(PlaybackParameters)
*/
public Builder setPlaybackSpeed(float playbackSpeed) {
return apply(new SetPlaybackSpeed(tag, playbackSpeed));
public Builder setPlaybackParameters(PlaybackParameters playbackParameters) {
return apply(new SetPlaybackParameters(tag, playbackParameters));
}
/**
......
......@@ -314,33 +314,17 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
throw new UnsupportedOperationException();
}
/** @deprecated Use {@link #setPlaybackSpeed(float)} instead. */
@SuppressWarnings("deprecation")
@Deprecated
@Override
public void setPlaybackParameters(@Nullable PlaybackParameters playbackParameters) {
throw new UnsupportedOperationException();
}
/** @deprecated Use {@link #getPlaybackSpeed()} instead. */
@SuppressWarnings("deprecation")
@Deprecated
@Override
public PlaybackParameters getPlaybackParameters() {
throw new UnsupportedOperationException();
}
@Override
public void setPlaybackSpeed(float playbackSpeed) {
throw new UnsupportedOperationException();
}
@Override
public float getPlaybackSpeed() {
throw new UnsupportedOperationException();
}
@Override
public void setSeekParameters(@Nullable SeekParameters seekParameters) {
throw new UnsupportedOperationException();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment