diff --git a/libraries/common/src/main/java/androidx/media3/common/audio/SpeedChangingAudioProcessor.java b/libraries/common/src/main/java/androidx/media3/common/audio/SpeedChangingAudioProcessor.java index 5e62497dee..f6e8e69e0e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/audio/SpeedChangingAudioProcessor.java +++ b/libraries/common/src/main/java/androidx/media3/common/audio/SpeedChangingAudioProcessor.java @@ -182,6 +182,9 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor { *

Calls {@linkplain LongConsumer#accept(long) the callback} with the output time as soon as * enough audio has been processed to calculate it. * + *

If the audio processor has ended, speeds will come out at the last processed speed of the + * audio processor. + * *

Successive calls must have monotonically increasing {@code inputTimeUs}. * *

Can be called from any thread. @@ -192,7 +195,7 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor { */ public void getSpeedAdjustedTimeAsync(long inputTimeUs, LongConsumer callback) { synchronized (pendingCallbacksLock) { - if (inputTimeUs <= lastProcessedInputTime) { + if (inputTimeUs <= lastProcessedInputTime || isEnded()) { callback.accept(calculateSpeedAdjustedTime(inputTimeUs)); return; } @@ -234,7 +237,7 @@ public final class SpeedChangingAudioProcessor extends BaseAudioProcessor { private void processPendingCallbacks() { synchronized (pendingCallbacksLock) { while (!pendingCallbacks.isEmpty() - && pendingCallbackInputTimesUs.element() <= lastProcessedInputTime) { + && (pendingCallbackInputTimesUs.element() <= lastProcessedInputTime || isEnded())) { pendingCallbacks .remove() .accept(calculateSpeedAdjustedTime(pendingCallbackInputTimesUs.remove())); diff --git a/libraries/common/src/test/java/androidx/media3/common/audio/SpeedChangingAudioProcessorTest.java b/libraries/common/src/test/java/androidx/media3/common/audio/SpeedChangingAudioProcessorTest.java index 84ee57bae4..b6df1aa4da 100644 --- a/libraries/common/src/test/java/androidx/media3/common/audio/SpeedChangingAudioProcessorTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/audio/SpeedChangingAudioProcessorTest.java @@ -16,6 +16,7 @@ package androidx.media3.common.audio; import static androidx.media3.common.audio.AudioProcessor.EMPTY_BUFFER; +import static androidx.media3.common.util.Assertions.checkState; import static com.google.common.truth.Truth.assertThat; import androidx.media3.common.C; @@ -378,6 +379,61 @@ public class SpeedChangingAudioProcessorTest { assertThat(outputTimesUs).containsExactly(25L, 50L, 93L); } + @Test + public void getSpeedAdjustedTimeAsync_timeAfterEndTime_callbacksCalledWithCorrectParameters() + throws Exception { + ArrayList outputTimesUs = new ArrayList<>(); + // The speed change is at 113Us (5*MICROS_PER_SECOND/sampleRate). + SpeedProvider speedProvider = + TestSpeedProvider.createWithFrameCounts( + AUDIO_FORMAT, /* frameCounts= */ new int[] {5, 5}, /* speeds= */ new float[] {2, 1}); + SpeedChangingAudioProcessor speedChangingAudioProcessor = + getConfiguredSpeedChangingAudioProcessor(speedProvider); + ByteBuffer inputBuffer = getInputBuffer(/* frameCount= */ 3); + + speedChangingAudioProcessor.getSpeedAdjustedTimeAsync( + /* inputTimeUs= */ 300L, outputTimesUs::add); + speedChangingAudioProcessor.queueInput(inputBuffer); + getAudioProcessorOutput(speedChangingAudioProcessor); + inputBuffer.rewind(); + speedChangingAudioProcessor.queueInput(inputBuffer); + getAudioProcessorOutput(speedChangingAudioProcessor); + inputBuffer.rewind(); + speedChangingAudioProcessor.queueInput(inputBuffer); + speedChangingAudioProcessor.queueEndOfStream(); + getAudioProcessorOutput(speedChangingAudioProcessor); + + // 150 is after the speed change so floor(113 / 2 + (300 - 113)*1) -> 243 + assertThat(outputTimesUs).containsExactly(243L); + } + + @Test + public void + getSpeedAdjustedTimeAsync_timeAfterEndTimeAfterProcessorEnded_callbacksCalledWithCorrectParameters() + throws Exception { + ArrayList outputTimesUs = new ArrayList<>(); + // The speed change is at 113Us (5*MICROS_PER_SECOND/sampleRate). + SpeedProvider speedProvider = + TestSpeedProvider.createWithFrameCounts( + AUDIO_FORMAT, /* frameCounts= */ new int[] {5, 5}, /* speeds= */ new float[] {2, 1}); + SpeedChangingAudioProcessor speedChangingAudioProcessor = + getConfiguredSpeedChangingAudioProcessor(speedProvider); + ByteBuffer inputBuffer = getInputBuffer(/* frameCount= */ 5); + speedChangingAudioProcessor.queueInput(inputBuffer); + getAudioProcessorOutput(speedChangingAudioProcessor); + inputBuffer.rewind(); + speedChangingAudioProcessor.queueInput(inputBuffer); + speedChangingAudioProcessor.queueEndOfStream(); + getAudioProcessorOutput(speedChangingAudioProcessor); + checkState(speedChangingAudioProcessor.isEnded()); + + speedChangingAudioProcessor.getSpeedAdjustedTimeAsync( + /* inputTimeUs= */ 300L, outputTimesUs::add); + + // 150 is after the speed change so floor(113 / 2 + (300 - 113)*1) -> 243 + assertThat(outputTimesUs).containsExactly(243L); + } + private static SpeedChangingAudioProcessor getConfiguredSpeedChangingAudioProcessor( SpeedProvider speedProvider) throws AudioProcessor.UnhandledAudioFormatException { SpeedChangingAudioProcessor speedChangingAudioProcessor =