diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java index e220128ac6..137517cdcb 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java @@ -162,7 +162,7 @@ public final class DefaultAudioSink implements AudioSink { private boolean canApplyPlaybackParameters; private int bufferSize; - @Nullable private PlaybackParameters drainingPlaybackParameters; + @Nullable private PlaybackParameters afterDrainPlaybackParameters; private PlaybackParameters playbackParameters; private long playbackParametersOffsetUs; private long playbackParametersPositionUs; @@ -521,22 +521,21 @@ public final class DefaultAudioSink implements AudioSink { } } - if (drainingPlaybackParameters != null) { + if (afterDrainPlaybackParameters != null) { if (!drainAudioProcessorsToEndOfStream()) { // Don't process any more input until draining completes. return false; } + PlaybackParameters newPlaybackParameters = afterDrainPlaybackParameters; + afterDrainPlaybackParameters = null; + newPlaybackParameters = applyPlaybackParameters(newPlaybackParameters); // Store the position and corresponding media time from which the parameters will apply. - playbackParametersCheckpoints.add(new PlaybackParametersCheckpoint( - drainingPlaybackParameters, Math.max(0, presentationTimeUs), - framesToDurationUs(getWrittenFrames()))); - drainingPlaybackParameters = null; - - // Flush the audio processors so that any new parameters take effect. - // TODO: Move parameter setting from setPlaybackParameters to here, so that it's not - // necessary to flush the processors twice. - sonicAudioProcessor.flush(); - silenceSkippingAudioProcessor.flush(); + playbackParametersCheckpoints.add( + new PlaybackParametersCheckpoint( + newPlaybackParameters, + Math.max(0, presentationTimeUs), + framesToDurationUs(getWrittenFrames()))); + // Update the set of active audio processors to take into account the new parameters. setupAudioProcessors(); } @@ -742,14 +741,9 @@ public final class DefaultAudioSink implements AudioSink { this.playbackParameters = PlaybackParameters.DEFAULT; return this.playbackParameters; } - playbackParameters = - new PlaybackParameters( - sonicAudioProcessor.setSpeed(playbackParameters.speed), - sonicAudioProcessor.setPitch(playbackParameters.pitch), - playbackParameters.skipSilence); - silenceSkippingAudioProcessor.setEnabled(playbackParameters.skipSilence); PlaybackParameters lastSetPlaybackParameters = - drainingPlaybackParameters != null ? drainingPlaybackParameters + afterDrainPlaybackParameters != null + ? afterDrainPlaybackParameters : !playbackParametersCheckpoints.isEmpty() ? playbackParametersCheckpoints.getLast().playbackParameters : this.playbackParameters; @@ -757,9 +751,10 @@ public final class DefaultAudioSink implements AudioSink { if (isInitialized()) { // Drain the audio processors so we can determine the frame position at which the new // parameters apply. - drainingPlaybackParameters = playbackParameters; + afterDrainPlaybackParameters = playbackParameters; } else { - this.playbackParameters = playbackParameters; + // Update the playback parameters now. + this.playbackParameters = applyPlaybackParameters(playbackParameters); } } return this.playbackParameters; @@ -845,9 +840,9 @@ public final class DefaultAudioSink implements AudioSink { writtenPcmBytes = 0; writtenEncodedFrames = 0; framesPerEncodedSample = 0; - if (drainingPlaybackParameters != null) { - playbackParameters = drainingPlaybackParameters; - drainingPlaybackParameters = null; + if (afterDrainPlaybackParameters != null) { + playbackParameters = afterDrainPlaybackParameters; + afterDrainPlaybackParameters = null; } else if (!playbackParametersCheckpoints.isEmpty()) { playbackParameters = playbackParametersCheckpoints.getLast().playbackParameters; } @@ -917,6 +912,21 @@ public final class DefaultAudioSink implements AudioSink { }.start(); } + /** + * Configures audio processors to apply the specified playback parameters, returning the new + * parameters, which may differ from those passed in. + * + * @param playbackParameters The playback parameters to try to apply. + * @return The playback parameters that were actually applied. + */ + private PlaybackParameters applyPlaybackParameters(PlaybackParameters playbackParameters) { + silenceSkippingAudioProcessor.setEnabled(playbackParameters.skipSilence); + return new PlaybackParameters( + sonicAudioProcessor.setSpeed(playbackParameters.speed), + sonicAudioProcessor.setPitch(playbackParameters.pitch), + playbackParameters.skipSilence); + } + /** * Returns the underlying audio track {@code positionUs} with any applicable speedup applied. */ diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java index 4d04f870b1..b748dbab5d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java @@ -72,7 +72,6 @@ import java.nio.ByteOrder; private int bytesPerFrame; private boolean enabled; - private boolean pendingEnabled; private ByteBuffer buffer; private ByteBuffer outputBuffer; @@ -108,13 +107,14 @@ import java.nio.ByteOrder; } /** - * Sets whether to skip silence in the input. The new setting will take effect after calling - * {@link #flush()}. + * Sets whether to skip silence in the input. Calling this method will discard any data buffered + * within the processor, and may update the value returned by {@link #isActive()}. * * @param enabled Whether to skip silence in the input. */ public void setEnabled(boolean enabled) { - pendingEnabled = enabled; + this.enabled = enabled; + flush(); } /** @@ -144,7 +144,7 @@ import java.nio.ByteOrder; @Override public boolean isActive() { - return enabled; + return sampleRateHz != Format.NO_VALUE && enabled; } @Override @@ -208,7 +208,6 @@ import java.nio.ByteOrder; @Override public void flush() { - enabled = pendingEnabled; if (isActive()) { int maybeSilenceBufferSize = durationUsToFrames(MINIMUM_SILENCE_DURATION_US) * bytesPerFrame; if (maybeSilenceBuffer.length != maybeSilenceBufferSize) { @@ -230,7 +229,6 @@ import java.nio.ByteOrder; @Override public void reset() { enabled = false; - pendingEnabled = false; flush(); buffer = EMPTY_BUFFER; channelCount = Format.NO_VALUE; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/SonicAudioProcessor.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/SonicAudioProcessor.java index 01f1869717..2ca2d47828 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/SonicAudioProcessor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/SonicAudioProcessor.java @@ -67,8 +67,6 @@ public final class SonicAudioProcessor implements AudioProcessor { private float speed; private float pitch; private int outputSampleRateHz; - private float pendingSpeed; - private float pendingPitch; private int pendingOutputSampleRateHz; private @Nullable Sonic sonic; @@ -85,8 +83,6 @@ public final class SonicAudioProcessor implements AudioProcessor { public SonicAudioProcessor() { speed = 1f; pitch = 1f; - pendingSpeed = 1f; - pendingPitch = 1f; channelCount = Format.NO_VALUE; sampleRateHz = Format.NO_VALUE; outputSampleRateHz = Format.NO_VALUE; @@ -97,25 +93,37 @@ public final class SonicAudioProcessor implements AudioProcessor { } /** - * Sets the playback speed. The new speed will take effect after a call to {@link #flush()}. + * Sets the playback speed. Calling this method will discard any data buffered within the + * processor, and may update the value returned by {@link #isActive()}. * * @param speed The requested new playback speed. * @return The actual new playback speed. */ public float setSpeed(float speed) { - pendingSpeed = Util.constrainValue(speed, MINIMUM_SPEED, MAXIMUM_SPEED); - return pendingSpeed; + speed = Util.constrainValue(speed, MINIMUM_SPEED, MAXIMUM_SPEED); + if (this.speed != speed) { + this.speed = speed; + sonic = null; + } + flush(); + return speed; } /** - * Sets the playback pitch. The new pitch will take effect after a call to {@link #flush()}. + * Sets the playback pitch. Calling this method will discard any data buffered within the + * processor, and may update the value returned by {@link #isActive()}. * * @param pitch The requested new pitch. * @return The actual new pitch. */ public float setPitch(float pitch) { - pendingPitch = Util.constrainValue(pitch, MINIMUM_PITCH, MAXIMUM_PITCH); - return pendingPitch; + pitch = Util.constrainValue(pitch, MINIMUM_PITCH, MAXIMUM_PITCH); + if (this.pitch != pitch) { + this.pitch = pitch; + sonic = null; + } + flush(); + return pitch; } /** @@ -170,8 +178,10 @@ public final class SonicAudioProcessor implements AudioProcessor { @Override public boolean isActive() { - return Math.abs(speed - 1f) >= CLOSE_THRESHOLD || Math.abs(pitch - 1f) >= CLOSE_THRESHOLD - || outputSampleRateHz != sampleRateHz; + return sampleRateHz != Format.NO_VALUE + && (Math.abs(speed - 1f) >= CLOSE_THRESHOLD + || Math.abs(pitch - 1f) >= CLOSE_THRESHOLD + || outputSampleRateHz != sampleRateHz); } @Override @@ -236,11 +246,8 @@ public final class SonicAudioProcessor implements AudioProcessor { @Override public void flush() { - boolean parametersChanged = pendingSpeed != speed || pendingPitch != pitch; - speed = pendingSpeed; - pitch = pendingPitch; if (isActive()) { - if (sonic == null || parametersChanged) { + if (sonic == null) { sonic = new Sonic(sampleRateHz, channelCount, speed, pitch, outputSampleRateHz); } else { sonic.flush(); @@ -256,8 +263,6 @@ public final class SonicAudioProcessor implements AudioProcessor { public void reset() { speed = 1f; pitch = 1f; - pendingSpeed = 1f; - pendingPitch = 1f; channelCount = Format.NO_VALUE; sampleRateHz = Format.NO_VALUE; outputSampleRateHz = Format.NO_VALUE;