diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java index 77b6877153..c03ad0f4af 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java @@ -263,18 +263,28 @@ public interface AudioSink { boolean hasPendingData(); /** - * 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. + * @deprecated Use {@link #setPlaybackSpeed(float)} and {@link #setSkipSilenceEnabled(boolean)} + * instead. */ + @Deprecated void setPlaybackParameters(PlaybackParameters playbackParameters); - /** - * Gets the active {@link PlaybackParameters}. - */ + /** @deprecated Use {@link #getPlaybackSpeed()} and {@link #getSkipSilenceEnabled()} instead. */ + @Deprecated PlaybackParameters getPlaybackParameters(); + /** Sets the playback speed. */ + void setPlaybackSpeed(float playbackSpeed); + + /** Gets the playback speed. */ + float getPlaybackSpeed(); + + /** Sets whether silences should be skipped in the audio stream. */ + void setSkipSilenceEnabled(boolean skipSilenceEnabled); + + /** Gets whether silences are skipped in the audio stream. */ + boolean getSkipSilenceEnabled(); + /** * Sets attributes for audio playback. If the attributes have changed and if the sink is not * configured for use with tunneling, then it is reset and the audio session id is cleared. 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 e9ae7d3033..574b862b4d 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 @@ -81,15 +81,31 @@ public final class DefaultAudioSink implements AudioSink { AudioProcessor[] getAudioProcessors(); /** - * Configures audio processors to apply the specified playback parameters immediately, returning - * the new parameters, which may differ from those passed in. Only called when processors have - * no input pending. - * - * @param playbackParameters The playback parameters to try to apply. - * @return The playback parameters that were actually applied. + * @deprecated Use {@link #applyPlaybackSpeed(float)} and {@link + * #applySkipSilenceEnabled(boolean)} instead. */ + @Deprecated PlaybackParameters applyPlaybackParameters(PlaybackParameters playbackParameters); + /** + * Configures audio processors to apply the specified playback speed immediately, returning the + * new playback speed, which may differ from the speed passed in. Only called when processors + * have no input pending. + * + * @param playbackSpeed The playback speed to try to apply. + * @return The playback speed that was actually applied. + */ + float applyPlaybackSpeed(float playbackSpeed); + + /** + * Configures audio processors to apply whether to skip silences immediately, returning the new + * value. Only called when processors have no input pending. + * + * @param skipSilenceEnabled Whether silences should be skipped in the audio stream. + * @return The new value. + */ + boolean applySkipSilenceEnabled(boolean skipSilenceEnabled); + /** * Scales the specified playout duration to take into account speedup due to audio processing, * returning an input media duration, in arbitrary units. @@ -138,12 +154,27 @@ public final class DefaultAudioSink implements AudioSink { return audioProcessors; } + /** + * @deprecated Use {@link #applyPlaybackSpeed(float)} and {@link + * #applySkipSilenceEnabled(boolean)} instead. + */ + @Deprecated @Override public PlaybackParameters applyPlaybackParameters(PlaybackParameters playbackParameters) { - silenceSkippingAudioProcessor.setEnabled(playbackParameters.skipSilence); return new PlaybackParameters( - sonicAudioProcessor.setSpeed(playbackParameters.speed), - playbackParameters.skipSilence); + applyPlaybackSpeed(playbackParameters.speed), + applySkipSilenceEnabled(playbackParameters.skipSilence)); + } + + @Override + public float applyPlaybackSpeed(float playbackSpeed) { + return sonicAudioProcessor.setSpeed(playbackSpeed); + } + + @Override + public boolean applySkipSilenceEnabled(boolean skipSilenceEnabled) { + silenceSkippingAudioProcessor.setEnabled(skipSilenceEnabled); + return skipSilenceEnabled; } @Override @@ -199,6 +230,10 @@ public final class DefaultAudioSink implements AudioSink { */ @SuppressLint("InlinedApi") private static final int WRITE_NON_BLOCKING = AudioTrack.WRITE_NON_BLOCKING; + /** The default playback speed. */ + private static final float DEFAULT_PLAYBACK_SPEED = 1.0f; + /** The default skip silence flag. */ + private static final boolean DEFAULT_SKIP_SILENCE = false; private static final String TAG = "AudioTrack"; @@ -240,7 +275,7 @@ public final class DefaultAudioSink implements AudioSink { private AudioTrack audioTrack; private AudioAttributes audioAttributes; - @Nullable private PlaybackParameters afterDrainPlaybackParameters; + @Nullable private MediaPositionParameters afterDrainParameters; private MediaPositionParameters mediaPositionParameters; @Nullable private ByteBuffer avSyncHeader; @@ -346,7 +381,10 @@ public final class DefaultAudioSink implements AudioSink { auxEffectInfo = new AuxEffectInfo(AuxEffectInfo.NO_AUX_EFFECT_ID, 0f); mediaPositionParameters = new MediaPositionParameters( - PlaybackParameters.DEFAULT, /* mediaTimeUs= */ 0, /* audioTrackPositionUs= */ 0); + DEFAULT_PLAYBACK_SPEED, + DEFAULT_SKIP_SILENCE, + /* mediaTimeUs= */ 0, + /* audioTrackPositionUs= */ 0); drainingAudioProcessorIndex = C.INDEX_UNSET; activeAudioProcessors = new AudioProcessor[0]; outputBuffers = new ByteBuffer[0]; @@ -528,7 +566,7 @@ public final class DefaultAudioSink implements AudioSink { startMediaTimeUs = Math.max(0, presentationTimeUs); startMediaTimeUsNeedsSync = false; - applyPlaybackParameters(getPlaybackParameters(), presentationTimeUs); + applyPlaybackSpeedAndSkipSilence(presentationTimeUs); audioTrackPositionTracker.setAudioTrack( audioTrack, @@ -582,7 +620,7 @@ public final class DefaultAudioSink implements AudioSink { pendingConfiguration = null; } // Re-apply playback parameters. - applyPlaybackParameters(getPlaybackParameters(), presentationTimeUs); + applyPlaybackSpeedAndSkipSilence(presentationTimeUs); } if (!isInitialized()) { @@ -615,14 +653,13 @@ public final class DefaultAudioSink implements AudioSink { } } - if (afterDrainPlaybackParameters != null) { + if (afterDrainParameters != null) { if (!drainToEndOfStream()) { // Don't process any more input until draining completes. return false; } - PlaybackParameters newPlaybackParameters = afterDrainPlaybackParameters; - afterDrainPlaybackParameters = null; - applyPlaybackParameters(newPlaybackParameters, presentationTimeUs); + applyPlaybackSpeedAndSkipSilence(presentationTimeUs); + afterDrainParameters = null; } // Sanity check that presentationTimeUs is consistent with the expected value. @@ -652,7 +689,7 @@ public final class DefaultAudioSink implements AudioSink { startMediaTimeUs += adjustmentUs; startMediaTimeUsNeedsSync = false; // Re-apply playback parameters because the startMediaTimeUs changed. - applyPlaybackParameters(getPlaybackParameters(), presentationTimeUs); + applyPlaybackSpeedAndSkipSilence(presentationTimeUs); if (listener != null && adjustmentUs != 0) { listener.onPositionDiscontinuity(); } @@ -825,35 +862,49 @@ public final class DefaultAudioSink implements AudioSink { return isInitialized() && audioTrackPositionTracker.hasPendingData(getWrittenFrames()); } + /** + * @deprecated Use {@link #setPlaybackSpeed(float)} and {@link #setSkipSilenceEnabled(boolean)} + * instead. + */ + @Deprecated @Override public void setPlaybackParameters(PlaybackParameters playbackParameters) { - if (configuration != null && !configuration.canApplyPlaybackParameters) { - playbackParameters = PlaybackParameters.DEFAULT; - } - PlaybackParameters lastSetPlaybackParameters = getPlaybackParameters(); - if (!playbackParameters.equals(lastSetPlaybackParameters)) { - if (isInitialized()) { - // Drain the audio processors so we can determine the frame position at which the new - // parameters apply. - afterDrainPlaybackParameters = playbackParameters; - } else { - // Update the playback parameters now. They will be applied to the audio processors during - // initialization. - mediaPositionParameters = - new MediaPositionParameters( - playbackParameters, /* mediaTimeUs= */ 0, /* audioTrackPositionUs= */ 0); - } - } + setPlaybackSpeedAndSkipSilence(playbackParameters.speed, playbackParameters.skipSilence); + } + + /** @deprecated Use {@link #getPlaybackSpeed()} and {@link #getSkipSilenceEnabled()} instead. */ + @Deprecated + @Override + public PlaybackParameters getPlaybackParameters() { + MediaPositionParameters mediaPositionParameters = getMediaPositionParameters(); + return new PlaybackParameters( + mediaPositionParameters.playbackSpeed, mediaPositionParameters.skipSilence); } @Override - public PlaybackParameters getPlaybackParameters() { - // Mask the already set parameters. - return afterDrainPlaybackParameters != null - ? afterDrainPlaybackParameters - : !mediaPositionParametersCheckpoints.isEmpty() - ? mediaPositionParametersCheckpoints.getLast().playbackParameters - : mediaPositionParameters.playbackParameters; + public void setPlaybackSpeed(float playbackSpeed) { + if (configuration != null && !configuration.canApplyPlaybackParameters) { + playbackSpeed = DEFAULT_PLAYBACK_SPEED; + } + setPlaybackSpeedAndSkipSilence(playbackSpeed, getSkipSilenceEnabled()); + } + + @Override + public float getPlaybackSpeed() { + return getMediaPositionParameters().playbackSpeed; + } + + @Override + public void setSkipSilenceEnabled(boolean skipSilenceEnabled) { + if (configuration != null && !configuration.canApplyPlaybackParameters) { + skipSilenceEnabled = DEFAULT_SKIP_SILENCE; + } + setPlaybackSpeedAndSkipSilence(getPlaybackSpeed(), skipSilenceEnabled); + } + + @Override + public boolean getSkipSilenceEnabled() { + return getMediaPositionParameters().skipSilence; } @Override @@ -951,9 +1002,12 @@ public final class DefaultAudioSink implements AudioSink { framesPerEncodedSample = 0; mediaPositionParameters = new MediaPositionParameters( - getPlaybackParameters(), /* mediaTimeUs= */ 0, /* audioTrackPositionUs= */ 0); + getPlaybackSpeed(), + getSkipSilenceEnabled(), + /* mediaTimeUs= */ 0, + /* audioTrackPositionUs= */ 0); startMediaTimeUs = 0; - afterDrainPlaybackParameters = null; + afterDrainParameters = null; mediaPositionParametersCheckpoints.clear(); trimmingAudioProcessor.resetTrimmedFrameCount(); flushAudioProcessors(); @@ -1005,9 +1059,9 @@ public final class DefaultAudioSink implements AudioSink { playing = false; } - /** - * Releases {@link #keepSessionIdAudioTrack} asynchronously, if it is non-{@code null}. - */ + // Internal methods. + + /** Releases {@link #keepSessionIdAudioTrack} asynchronously, if it is non-{@code null}. */ private void releaseKeepSessionIdAudioTrack() { if (keepSessionIdAudioTrack == null) { return; @@ -1024,15 +1078,54 @@ public final class DefaultAudioSink implements AudioSink { }.start(); } - private void applyPlaybackParameters( - PlaybackParameters playbackParameters, long presentationTimeUs) { - PlaybackParameters newPlaybackParameters = + private void setPlaybackSpeedAndSkipSilence(float playbackSpeed, boolean skipSilence) { + if (configuration != null && !configuration.canApplyPlaybackParameters) { + playbackSpeed = DEFAULT_PLAYBACK_SPEED; + skipSilence = DEFAULT_SKIP_SILENCE; + } + MediaPositionParameters currentMediaPositionParameters = getMediaPositionParameters(); + if (playbackSpeed != currentMediaPositionParameters.playbackSpeed + || skipSilence != currentMediaPositionParameters.skipSilence) { + MediaPositionParameters mediaPositionParameters = + new MediaPositionParameters( + playbackSpeed, + skipSilence, + /* mediaTimeUs= */ C.TIME_UNSET, + /* audioTrackPositionUs= */ C.TIME_UNSET); + if (isInitialized()) { + // Drain the audio processors so we can determine the frame position at which the new + // parameters apply. + this.afterDrainParameters = mediaPositionParameters; + } else { + // Update the audio processor chain parameters now. They will be applied to the audio + // processors during initialization. + this.mediaPositionParameters = mediaPositionParameters; + } + } + } + + private MediaPositionParameters getMediaPositionParameters() { + // Mask the already set parameters. + return afterDrainParameters != null + ? afterDrainParameters + : !mediaPositionParametersCheckpoints.isEmpty() + ? mediaPositionParametersCheckpoints.getLast() + : mediaPositionParameters; + } + + private void applyPlaybackSpeedAndSkipSilence(long presentationTimeUs) { + float playbackSpeed = configuration.canApplyPlaybackParameters - ? audioProcessorChain.applyPlaybackParameters(playbackParameters) - : PlaybackParameters.DEFAULT; + ? audioProcessorChain.applyPlaybackSpeed(getPlaybackSpeed()) + : DEFAULT_PLAYBACK_SPEED; + boolean skipSilence = + configuration.canApplyPlaybackParameters + ? audioProcessorChain.applySkipSilenceEnabled(getSkipSilenceEnabled()) + : DEFAULT_SKIP_SILENCE; mediaPositionParametersCheckpoints.add( new MediaPositionParameters( - newPlaybackParameters, + playbackSpeed, + skipSilence, /* mediaTimeUs= */ Math.max(0, presentationTimeUs), /* audioTrackPositionUs= */ configuration.framesToDurationUs(getWrittenFrames()))); setupAudioProcessors(); @@ -1053,7 +1146,7 @@ public final class DefaultAudioSink implements AudioSink { long playoutDurationSinceLastCheckpoint = positionUs - mediaPositionParameters.audioTrackPositionUs; - if (mediaPositionParameters.playbackParameters.speed != 1f) { + if (mediaPositionParameters.playbackSpeed != 1f) { if (mediaPositionParametersCheckpoints.isEmpty()) { playoutDurationSinceLastCheckpoint = audioProcessorChain.getMediaDuration(playoutDurationSinceLastCheckpoint); @@ -1061,8 +1154,7 @@ public final class DefaultAudioSink implements AudioSink { // Playing data at a previous playback speed, so fall back to multiplying by the speed. playoutDurationSinceLastCheckpoint = Util.getMediaDurationForPlayoutDuration( - playoutDurationSinceLastCheckpoint, - mediaPositionParameters.playbackParameters.speed); + playoutDurationSinceLastCheckpoint, mediaPositionParameters.playbackSpeed); } } return mediaPositionParameters.mediaTimeUs + playoutDurationSinceLastCheckpoint; @@ -1248,16 +1340,19 @@ public final class DefaultAudioSink implements AudioSink { /** Stores parameters used to calculate the current media position. */ private static final class MediaPositionParameters { - /** The playback parameters. */ - public final PlaybackParameters playbackParameters; + /** The playback speed. */ + public final float playbackSpeed; + /** Whether to skip silences. */ + public final boolean skipSilence; /** The media time from which the playback parameters apply, in microseconds. */ public final long mediaTimeUs; /** The audio track position from which the playback parameters apply, in microseconds. */ public final long audioTrackPositionUs; private MediaPositionParameters( - PlaybackParameters playbackParameters, long mediaTimeUs, long audioTrackPositionUs) { - this.playbackParameters = playbackParameters; + float playbackSpeed, boolean skipSilence, long mediaTimeUs, long audioTrackPositionUs) { + this.playbackSpeed = playbackSpeed; + this.skipSilence = skipSilence; this.mediaTimeUs = mediaTimeUs; this.audioTrackPositionUs = audioTrackPositionUs; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/ForwardingAudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/ForwardingAudioSink.java index 5b06d1cea4..fefaa6db23 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/ForwardingAudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/ForwardingAudioSink.java @@ -95,14 +95,42 @@ public class ForwardingAudioSink implements AudioSink { return sink.hasPendingData(); } + /** + * @deprecated Use {@link #setPlaybackSpeed(float)} and {@link #setSkipSilenceEnabled(boolean)} + * instead. + */ + @Deprecated @Override public void setPlaybackParameters(PlaybackParameters playbackParameters) { - sink.setPlaybackParameters(playbackParameters); + sink.setPlaybackSpeed(playbackParameters.speed); + sink.setSkipSilenceEnabled(playbackParameters.skipSilence); + } + + /** @deprecated Use {@link #getPlaybackSpeed()} and {@link #getSkipSilenceEnabled()} instead. */ + @Deprecated + @Override + public PlaybackParameters getPlaybackParameters() { + return new PlaybackParameters(sink.getPlaybackSpeed(), sink.getSkipSilenceEnabled()); } @Override - public PlaybackParameters getPlaybackParameters() { - return sink.getPlaybackParameters(); + public void setPlaybackSpeed(float playbackSpeed) { + sink.setPlaybackSpeed(playbackSpeed); + } + + @Override + public float getPlaybackSpeed() { + return sink.getPlaybackSpeed(); + } + + @Override + public void setSkipSilenceEnabled(boolean skipSilenceEnabled) { + sink.setSkipSilenceEnabled(skipSilenceEnabled); + } + + @Override + public boolean getSkipSilenceEnabled() { + return sink.getSkipSilenceEnabled(); } @Override diff --git a/library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java b/library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java index c110a3f6cc..9689a326e7 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java @@ -21,7 +21,6 @@ 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.PlaybackParameters; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; @@ -89,8 +88,7 @@ public final class DefaultAudioSinkTest { @Test public void handlesBufferAfterReset_withPlaybackParameters() throws Exception { - PlaybackParameters playbackParameters = new PlaybackParameters(1.5f); - defaultAudioSink.setPlaybackParameters(playbackParameters); + defaultAudioSink.setPlaybackSpeed(/* playbackSpeed= */ 1.5f); configureDefaultAudioSink(CHANNEL_COUNT_STEREO); defaultAudioSink.handleBuffer( createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1); @@ -100,7 +98,7 @@ public final class DefaultAudioSinkTest { configureDefaultAudioSink(CHANNEL_COUNT_STEREO); defaultAudioSink.handleBuffer( createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1); - assertThat(defaultAudioSink.getPlaybackParameters()).isEqualTo(playbackParameters); + assertThat(defaultAudioSink.getPlaybackSpeed()).isEqualTo(1.5f); } @Test @@ -118,8 +116,7 @@ public final class DefaultAudioSinkTest { @Test public void handlesBufferAfterReset_withFormatChangeAndPlaybackParameters() throws Exception { - PlaybackParameters playbackParameters = new PlaybackParameters(1.5f); - defaultAudioSink.setPlaybackParameters(playbackParameters); + defaultAudioSink.setPlaybackSpeed(/* playbackSpeed= */ 1.5f); configureDefaultAudioSink(CHANNEL_COUNT_STEREO); defaultAudioSink.handleBuffer( createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1); @@ -129,7 +126,7 @@ public final class DefaultAudioSinkTest { configureDefaultAudioSink(CHANNEL_COUNT_MONO); defaultAudioSink.handleBuffer( createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1); - assertThat(defaultAudioSink.getPlaybackParameters()).isEqualTo(playbackParameters); + assertThat(defaultAudioSink.getPlaybackSpeed()).isEqualTo(1.5f); } @Test