mirror of
https://github.com/androidx/media.git
synced 2025-05-07 23:50:44 +08:00
Apply new playback parameters only once drained
Previously the SonicAudioProcessor and SilenceSkippingAudioProcessor would track their pending playback parameters and only apply them in flush(). Having the values only take effect once flushed made the processors a bit more difficult to use, especially because the value returned by isActive wouldn't update immediately. Make DefaultAudioSink only set the new speed/pitch/skip silence setting after the audio processors have drained. This means it's no longer necessary to keep track of pending parameter values and also fixes a bug where initial playback parameters weren't applied because the audio processors weren't flushed while uninitialized before DefaultAudioSink called isActive() on them. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191586727
This commit is contained in:
parent
7c65b94578
commit
e3eddc4d20
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user