mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
parent
6193f7c38f
commit
f60d2b4146
2 changed files with 21 additions and 61 deletions
|
|
@ -24,6 +24,7 @@ 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;
|
||||||
|
|
@ -44,8 +45,10 @@ 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 implements AudioProcessor {
|
public final class SpeedChangingAudioProcessor extends BaseAudioProcessor {
|
||||||
|
|
||||||
private final Object lock;
|
private final Object lock;
|
||||||
|
|
||||||
|
|
@ -94,18 +97,7 @@ public final class SpeedChangingAudioProcessor implements AudioProcessor {
|
||||||
|
|
||||||
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 =
|
||||||
|
|
@ -113,7 +105,7 @@ public final class SpeedChangingAudioProcessor implements AudioProcessor {
|
||||||
pendingCallbackInputTimesUs = new LongArrayQueue();
|
pendingCallbackInputTimesUs = new LongArrayQueue();
|
||||||
pendingCallbacks = new ArrayDeque<>();
|
pendingCallbacks = new ArrayDeque<>();
|
||||||
speedAdjustedTimeAsyncInputTimeUs = C.TIME_UNSET;
|
speedAdjustedTimeAsyncInputTimeUs = C.TIME_UNSET;
|
||||||
resetInternalState();
|
resetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the estimated number of samples output given the provided parameters. */
|
/** Returns the estimated number of samples output given the provided parameters. */
|
||||||
|
|
@ -152,23 +144,17 @@ public final class SpeedChangingAudioProcessor implements AudioProcessor {
|
||||||
return outputSamples;
|
return outputSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public AudioFormat configure(AudioFormat inputAudioFormat) throws UnhandledAudioFormatException {
|
|
||||||
pendingInputAudioFormat = inputAudioFormat;
|
|
||||||
pendingOutputAudioFormat = sonicAudioProcessor.configure(inputAudioFormat);
|
|
||||||
return pendingOutputAudioFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isActive() {
|
|
||||||
return !pendingOutputAudioFormat.equals(AudioFormat.NOT_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getDurationAfterProcessorApplied(long durationUs) {
|
public long getDurationAfterProcessorApplied(long durationUs) {
|
||||||
return SpeedProviderUtil.getDurationAfterSpeedProviderApplied(speedProvider, durationUs);
|
return SpeedProviderUtil.getDurationAfterSpeedProviderApplied(speedProvider, durationUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AudioFormat onConfigure(AudioFormat inputAudioFormat)
|
||||||
|
throws UnhandledAudioFormatException {
|
||||||
|
return sonicAudioProcessor.configure(inputAudioFormat);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queueInput(ByteBuffer inputBuffer) {
|
public void queueInput(ByteBuffer inputBuffer) {
|
||||||
long currentTimeUs = sampleCountToDurationUs(framesRead, inputAudioFormat.sampleRate);
|
long currentTimeUs = sampleCountToDurationUs(framesRead, inputAudioFormat.sampleRate);
|
||||||
|
|
@ -205,14 +191,15 @@ public final class SpeedChangingAudioProcessor implements AudioProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queueEndOfStream() {
|
protected void onQueueEndOfStream() {
|
||||||
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();
|
||||||
|
|
@ -222,24 +209,18 @@ public final class SpeedChangingAudioProcessor implements AudioProcessor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnded() {
|
public boolean isEnded() {
|
||||||
return inputEnded && sonicAudioProcessor.isEnded();
|
return super.isEnded() && sonicAudioProcessor.isEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flush() {
|
protected void onFlush() {
|
||||||
inputEnded = false;
|
resetState();
|
||||||
inputAudioFormat = pendingInputAudioFormat;
|
|
||||||
resetInternalState();
|
|
||||||
sonicAudioProcessor.flush();
|
sonicAudioProcessor.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
protected void onReset() {
|
||||||
flush();
|
resetState();
|
||||||
pendingInputAudioFormat = AudioFormat.NOT_SET;
|
|
||||||
pendingOutputAudioFormat = AudioFormat.NOT_SET;
|
|
||||||
inputAudioFormat = AudioFormat.NOT_SET;
|
|
||||||
resetInternalState();
|
|
||||||
sonicAudioProcessor.reset();
|
sonicAudioProcessor.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -371,6 +352,7 @@ public final class SpeedChangingAudioProcessor implements AudioProcessor {
|
||||||
// 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -414,7 +396,7 @@ public final class SpeedChangingAudioProcessor implements AudioProcessor {
|
||||||
|
|
||||||
@EnsuresNonNull({"inputSegmentStartTimesUs", "outputSegmentStartTimesUs"})
|
@EnsuresNonNull({"inputSegmentStartTimesUs", "outputSegmentStartTimesUs"})
|
||||||
@RequiresNonNull("lock")
|
@RequiresNonNull("lock")
|
||||||
private void resetInternalState(@UnknownInitialization SpeedChangingAudioProcessor this) {
|
private void resetState(@UnknownInitialization SpeedChangingAudioProcessor this) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
inputSegmentStartTimesUs = new LongArray();
|
inputSegmentStartTimesUs = new LongArray();
|
||||||
outputSegmentStartTimesUs = new LongArray();
|
outputSegmentStartTimesUs = new LongArray();
|
||||||
|
|
|
||||||
|
|
@ -701,28 +701,6 @@ 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 =
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue