Commit bc8fd2ca by bachinger Committed by Oliver Woodman

split and deprecate PlaybackParameters in AudioSink

This change deprecates PlaybackParameter in AudioSink and splits it into playbackSpeed and skipSilenceEnabled. These properties are set separately in a future CL. The playback speed will be set through the MediaClock, while skipSilenceEnabled will be set by sending a message to the audio renderer.

PiperOrigin-RevId: 296457043
parent 42d4afe7
......@@ -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.
......
......@@ -81,16 +81,32 @@ 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) {
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 void setPlaybackSpeed(float playbackSpeed) {
if (configuration != null && !configuration.canApplyPlaybackParameters) {
playbackParameters = PlaybackParameters.DEFAULT;
playbackSpeed = DEFAULT_PLAYBACK_SPEED;
}
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(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 PlaybackParameters getPlaybackParameters() {
// Mask the already set parameters.
return afterDrainPlaybackParameters != null
? afterDrainPlaybackParameters
: !mediaPositionParametersCheckpoints.isEmpty()
? mediaPositionParametersCheckpoints.getLast().playbackParameters
: mediaPositionParameters.playbackParameters;
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.applyPlaybackSpeed(getPlaybackSpeed())
: DEFAULT_PLAYBACK_SPEED;
boolean skipSilence =
configuration.canApplyPlaybackParameters
? audioProcessorChain.applyPlaybackParameters(playbackParameters)
: PlaybackParameters.DEFAULT;
? 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;
}
......
......@@ -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 sink.getPlaybackParameters();
return new PlaybackParameters(sink.getPlaybackSpeed(), sink.getSkipSilenceEnabled());
}
@Override
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
......
......@@ -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
......
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