PiperOrigin-RevId: 710998501
This commit is contained in:
ivanbuper 2024-12-31 10:23:36 -08:00 committed by Copybara-Service
parent d6e4642bcf
commit 48e3c6fd75
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.min;
import static java.lang.Math.round; import static java.lang.Math.round;
import android.annotation.SuppressLint;
import androidx.annotation.GuardedBy; import androidx.annotation.GuardedBy;
import androidx.annotation.IntRange; import androidx.annotation.IntRange;
import androidx.media3.common.C; 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. * 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 @UnstableApi
public final class SpeedChangingAudioProcessor extends BaseAudioProcessor { public final class SpeedChangingAudioProcessor implements AudioProcessor {
private final Object lock; private final Object lock;
@ -97,7 +94,18 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
private boolean endOfStreamQueuedToSonic; private boolean endOfStreamQueuedToSonic;
/** The current input audio format. */
private AudioFormat inputAudioFormat;
private AudioFormat pendingInputAudioFormat;
private AudioFormat pendingOutputAudioFormat;
private boolean inputEnded;
public SpeedChangingAudioProcessor(SpeedProvider speedProvider) { public SpeedChangingAudioProcessor(SpeedProvider speedProvider) {
pendingInputAudioFormat = AudioFormat.NOT_SET;
pendingOutputAudioFormat = AudioFormat.NOT_SET;
inputAudioFormat = AudioFormat.NOT_SET;
this.speedProvider = speedProvider; this.speedProvider = speedProvider;
lock = new Object(); lock = new Object();
sonicAudioProcessor = sonicAudioProcessor =
@ -105,7 +113,7 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
pendingCallbackInputTimesUs = new LongArrayQueue(); pendingCallbackInputTimesUs = new LongArrayQueue();
pendingCallbacks = new ArrayDeque<>(); pendingCallbacks = new ArrayDeque<>();
speedAdjustedTimeAsyncInputTimeUs = C.TIME_UNSET; speedAdjustedTimeAsyncInputTimeUs = C.TIME_UNSET;
resetState(/* shouldResetSpeed= */ true); resetInternalState(/* shouldResetSpeed= */ true);
} }
/** Returns the estimated number of samples output given the provided parameters. */ /** Returns the estimated number of samples output given the provided parameters. */
@ -145,14 +153,20 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
} }
@Override @Override
public long getDurationAfterProcessorApplied(long durationUs) { public AudioFormat configure(AudioFormat inputAudioFormat) throws UnhandledAudioFormatException {
return SpeedProviderUtil.getDurationAfterSpeedProviderApplied(speedProvider, durationUs); pendingInputAudioFormat = inputAudioFormat;
pendingOutputAudioFormat = sonicAudioProcessor.configure(inputAudioFormat);
return pendingOutputAudioFormat;
} }
@Override @Override
public AudioFormat onConfigure(AudioFormat inputAudioFormat) public boolean isActive() {
throws UnhandledAudioFormatException { return !pendingOutputAudioFormat.equals(AudioFormat.NOT_SET);
return sonicAudioProcessor.configure(inputAudioFormat); }
@Override
public long getDurationAfterProcessorApplied(long durationUs) {
return SpeedProviderUtil.getDurationAfterSpeedProviderApplied(speedProvider, durationUs);
} }
@Override @Override
@ -191,15 +205,14 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
} }
@Override @Override
protected void onQueueEndOfStream() { public void queueEndOfStream() {
inputEnded = true;
if (!endOfStreamQueuedToSonic) { if (!endOfStreamQueuedToSonic) {
sonicAudioProcessor.queueEndOfStream(); sonicAudioProcessor.queueEndOfStream();
endOfStreamQueuedToSonic = true; endOfStreamQueuedToSonic = true;
} }
} }
// Not using BaseAudioProcessor's buffers.
@SuppressLint("MissingSuperCall")
@Override @Override
public ByteBuffer getOutput() { public ByteBuffer getOutput() {
ByteBuffer output = sonicAudioProcessor.getOutput(); ByteBuffer output = sonicAudioProcessor.getOutput();
@ -209,18 +222,24 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
@Override @Override
public boolean isEnded() { public boolean isEnded() {
return super.isEnded() && sonicAudioProcessor.isEnded(); return inputEnded && sonicAudioProcessor.isEnded();
} }
@Override @Override
protected void onFlush() { public void flush() {
resetState(/* shouldResetSpeed= */ false); inputEnded = false;
inputAudioFormat = pendingInputAudioFormat;
resetInternalState(/* shouldResetSpeed= */ false);
sonicAudioProcessor.flush(); sonicAudioProcessor.flush();
} }
@Override @Override
protected void onReset() { public void reset() {
resetState(/* shouldResetSpeed= */ true); flush();
pendingInputAudioFormat = AudioFormat.NOT_SET;
pendingOutputAudioFormat = AudioFormat.NOT_SET;
inputAudioFormat = AudioFormat.NOT_SET;
resetInternalState(/* shouldResetSpeed= */ true);
sonicAudioProcessor.reset(); sonicAudioProcessor.reset();
} }
@ -352,7 +371,6 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
// Invalidate any previously created buffers in SonicAudioProcessor and the base class. // Invalidate any previously created buffers in SonicAudioProcessor and the base class.
sonicAudioProcessor.flush(); sonicAudioProcessor.flush();
endOfStreamQueuedToSonic = false; endOfStreamQueuedToSonic = false;
super.getOutput();
} }
} }
} }
@ -404,7 +422,7 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
*/ */
@EnsuresNonNull({"inputSegmentStartTimesUs", "outputSegmentStartTimesUs"}) @EnsuresNonNull({"inputSegmentStartTimesUs", "outputSegmentStartTimesUs"})
@RequiresNonNull("lock") @RequiresNonNull("lock")
private void resetState( private void resetInternalState(
@UnknownInitialization SpeedChangingAudioProcessor this, boolean shouldResetSpeed) { @UnknownInitialization SpeedChangingAudioProcessor this, boolean shouldResetSpeed) {
synchronized (lock) { synchronized (lock) {
inputSegmentStartTimesUs = new LongArray(); inputSegmentStartTimesUs = new LongArray();

View File

@ -779,6 +779,28 @@ public class SpeedChangingAudioProcessorTest {
/* speedProvider= */ null, AUDIO_FORMAT.sampleRate, /* inputSamples= */ 1000L)); /* 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( private static SpeedChangingAudioProcessor getConfiguredSpeedChangingAudioProcessor(
SpeedProvider speedProvider) throws AudioProcessor.UnhandledAudioFormatException { SpeedProvider speedProvider) throws AudioProcessor.UnhandledAudioFormatException {
SpeedChangingAudioProcessor speedChangingAudioProcessor = SpeedChangingAudioProcessor speedChangingAudioProcessor =