Make SpeedChangingAudioProcessor direct subclass of AudioProcessor

This non-functional refactor inlines and simplifies the logic of
`BaseAudioProcessor` used by `SpeedChangingAudioProcessor`, and makes
the latter implement `AudioProcessor` directly.

`SpeedChangingAudioProcessor` acts as a wrapper over
`SonicAudioProcessor` and does not actually modify any samples,
so it had very little use for the affordances provided by
`BaseAudioProcessor`.

PiperOrigin-RevId: 698342962
This commit is contained in:
ivanbuper 2024-11-20 04:24:47 -08:00 committed by Copybara-Service
parent 3c01500a4e
commit 66e8b53b43
2 changed files with 60 additions and 20 deletions

View File

@ -24,7 +24,6 @@ import static androidx.media3.common.util.Util.sampleCountToDurationUs;
import static java.lang.Math.min;
import static java.lang.Math.round;
import android.annotation.SuppressLint;
import androidx.annotation.GuardedBy;
import androidx.annotation.IntRange;
import androidx.media3.common.C;
@ -45,10 +44,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/**
* An {@link AudioProcessor} that changes the speed of audio samples depending on their timestamp.
*/
// TODO(b/288221200): Consider making the processor inactive and skipping it in the processor chain
// when speed is 1.
@UnstableApi
public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
public final class SpeedChangingAudioProcessor implements AudioProcessor {
private final Object lock;
@ -97,7 +94,18 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
private boolean endOfStreamQueuedToSonic;
/** The current input audio format. */
private AudioFormat inputAudioFormat;
private AudioFormat pendingInputAudioFormat;
private AudioFormat pendingOutputAudioFormat;
private boolean inputEnded;
public SpeedChangingAudioProcessor(SpeedProvider speedProvider) {
pendingInputAudioFormat = AudioFormat.NOT_SET;
pendingOutputAudioFormat = AudioFormat.NOT_SET;
inputAudioFormat = AudioFormat.NOT_SET;
this.speedProvider = speedProvider;
lock = new Object();
sonicAudioProcessor =
@ -105,7 +113,7 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
pendingCallbackInputTimesUs = new LongArrayQueue();
pendingCallbacks = new ArrayDeque<>();
speedAdjustedTimeAsyncInputTimeUs = C.TIME_UNSET;
resetState();
resetInternalState();
}
/** Returns the estimated number of samples output given the provided parameters. */
@ -145,14 +153,20 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
}
@Override
public long getDurationAfterProcessorApplied(long durationUs) {
return SpeedProviderUtil.getDurationAfterSpeedProviderApplied(speedProvider, durationUs);
public AudioFormat configure(AudioFormat inputAudioFormat) throws UnhandledAudioFormatException {
pendingInputAudioFormat = inputAudioFormat;
pendingOutputAudioFormat = sonicAudioProcessor.configure(inputAudioFormat);
return pendingOutputAudioFormat;
}
@Override
public AudioFormat onConfigure(AudioFormat inputAudioFormat)
throws UnhandledAudioFormatException {
return sonicAudioProcessor.configure(inputAudioFormat);
public boolean isActive() {
return !pendingOutputAudioFormat.equals(AudioFormat.NOT_SET);
}
@Override
public long getDurationAfterProcessorApplied(long durationUs) {
return SpeedProviderUtil.getDurationAfterSpeedProviderApplied(speedProvider, durationUs);
}
@Override
@ -191,15 +205,14 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
}
@Override
protected void onQueueEndOfStream() {
public void queueEndOfStream() {
inputEnded = true;
if (!endOfStreamQueuedToSonic) {
sonicAudioProcessor.queueEndOfStream();
endOfStreamQueuedToSonic = true;
}
}
// Not using BaseAudioProcessor's buffers.
@SuppressLint("MissingSuperCall")
@Override
public ByteBuffer getOutput() {
ByteBuffer output = sonicAudioProcessor.getOutput();
@ -209,18 +222,24 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
@Override
public boolean isEnded() {
return super.isEnded() && sonicAudioProcessor.isEnded();
return inputEnded && sonicAudioProcessor.isEnded();
}
@Override
protected void onFlush() {
resetState();
public void flush() {
inputEnded = false;
inputAudioFormat = pendingInputAudioFormat;
resetInternalState();
sonicAudioProcessor.flush();
}
@Override
protected void onReset() {
resetState();
public void reset() {
flush();
pendingInputAudioFormat = AudioFormat.NOT_SET;
pendingOutputAudioFormat = AudioFormat.NOT_SET;
inputAudioFormat = AudioFormat.NOT_SET;
resetInternalState();
sonicAudioProcessor.reset();
}
@ -352,7 +371,6 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
// Invalidate any previously created buffers in SonicAudioProcessor and the base class.
sonicAudioProcessor.flush();
endOfStreamQueuedToSonic = false;
super.getOutput();
}
}
}
@ -396,7 +414,7 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
@EnsuresNonNull({"inputSegmentStartTimesUs", "outputSegmentStartTimesUs"})
@RequiresNonNull("lock")
private void resetState(@UnknownInitialization SpeedChangingAudioProcessor this) {
private void resetInternalState(@UnknownInitialization SpeedChangingAudioProcessor this) {
synchronized (lock) {
inputSegmentStartTimesUs = new LongArray();
outputSegmentStartTimesUs = new LongArray();

View File

@ -701,6 +701,28 @@ public class SpeedChangingAudioProcessorTest {
/* speedProvider= */ null, AUDIO_FORMAT.sampleRate, /* inputSamples= */ 1000L));
}
@Test
public void isActive_beforeConfigure_returnsFalse() {
SpeedProvider speedProvider =
TestSpeedProvider.createWithFrameCounts(
AUDIO_FORMAT, /* frameCounts= */ new int[] {1000}, /* speeds= */ new float[] {2f});
SpeedChangingAudioProcessor processor = new SpeedChangingAudioProcessor(speedProvider);
assertThat(processor.isActive()).isFalse();
}
@Test
public void isActive_afterConfigure_returnsTrue()
throws AudioProcessor.UnhandledAudioFormatException {
SpeedProvider speedProvider =
TestSpeedProvider.createWithFrameCounts(
AUDIO_FORMAT, /* frameCounts= */ new int[] {1000}, /* speeds= */ new float[] {2f});
SpeedChangingAudioProcessor processor = new SpeedChangingAudioProcessor(speedProvider);
processor.configure(AUDIO_FORMAT);
assertThat(processor.isActive()).isTrue();
}
private static SpeedChangingAudioProcessor getConfiguredSpeedChangingAudioProcessor(
SpeedProvider speedProvider) throws AudioProcessor.UnhandledAudioFormatException {
SpeedChangingAudioProcessor speedChangingAudioProcessor =