mirror of
https://github.com/Pinball3D/Rabbit-R1.git
synced 2024-12-30 11:02:31 -06:00
348 lines
15 KiB
Java
348 lines
15 KiB
Java
package androidx.media3.exoplayer.audio;
|
|
|
|
import android.media.AudioTrack;
|
|
import android.os.SystemClock;
|
|
import androidx.media3.common.util.Assertions;
|
|
import androidx.media3.common.util.Util;
|
|
import io.flutter.embedding.android.KeyboardMap;
|
|
import java.lang.reflect.Method;
|
|
|
|
/* loaded from: classes2.dex */
|
|
final class AudioTrackPositionTracker {
|
|
private static final long FORCE_RESET_WORKAROUND_TIMEOUT_MS = 200;
|
|
private static final long MAX_AUDIO_TIMESTAMP_OFFSET_US = 5000000;
|
|
private static final long MAX_LATENCY_US = 5000000;
|
|
private static final int MAX_PLAYHEAD_OFFSET_COUNT = 10;
|
|
private static final int MIN_LATENCY_SAMPLE_INTERVAL_US = 500000;
|
|
private static final int MIN_PLAYHEAD_OFFSET_SAMPLE_INTERVAL_US = 30000;
|
|
private static final long MODE_SWITCH_SMOOTHING_DURATION_US = 1000000;
|
|
private static final int PLAYSTATE_PAUSED = 2;
|
|
private static final int PLAYSTATE_PLAYING = 3;
|
|
private static final int PLAYSTATE_STOPPED = 1;
|
|
private static final long RAW_PLAYBACK_HEAD_POSITION_UPDATE_INTERVAL_MS = 5;
|
|
private AudioTimestampPoller audioTimestampPoller;
|
|
private AudioTrack audioTrack;
|
|
private float audioTrackPlaybackSpeed;
|
|
private int bufferSize;
|
|
private long bufferSizeUs;
|
|
private long endPlaybackHeadPosition;
|
|
private long forceResetWorkaroundTimeMs;
|
|
private Method getLatencyMethod;
|
|
private boolean hasData;
|
|
private boolean isOutputPcm;
|
|
private long lastLatencySampleTimeUs;
|
|
private long lastPlayheadSampleTimeUs;
|
|
private long lastPositionUs;
|
|
private long lastRawPlaybackHeadPositionSampleTimeMs;
|
|
private boolean lastSampleUsedGetTimestampMode;
|
|
private long lastSystemTimeUs;
|
|
private long latencyUs;
|
|
private final Listener listener;
|
|
private boolean needsPassthroughWorkarounds;
|
|
private int nextPlayheadOffsetIndex;
|
|
private boolean notifiedPositionIncreasing;
|
|
private int outputPcmFrameSize;
|
|
private int outputSampleRate;
|
|
private long passthroughWorkaroundPauseOffset;
|
|
private int playheadOffsetCount;
|
|
private final long[] playheadOffsets;
|
|
private long previousModePositionUs;
|
|
private long previousModeSystemTimeUs;
|
|
private long rawPlaybackHeadPosition;
|
|
private long rawPlaybackHeadWrapCount;
|
|
private long smoothedPlayheadOffsetUs;
|
|
private long stopPlaybackHeadPosition;
|
|
private long stopTimestampUs;
|
|
|
|
/* loaded from: classes2.dex */
|
|
public interface Listener {
|
|
void onInvalidLatency(long j);
|
|
|
|
void onPositionAdvancing(long j);
|
|
|
|
void onPositionFramesMismatch(long j, long j2, long j3, long j4);
|
|
|
|
void onSystemTimeUsMismatch(long j, long j2, long j3, long j4);
|
|
|
|
void onUnderrun(int i, long j);
|
|
}
|
|
|
|
private void resetSyncParams() {
|
|
this.smoothedPlayheadOffsetUs = 0L;
|
|
this.playheadOffsetCount = 0;
|
|
this.nextPlayheadOffsetIndex = 0;
|
|
this.lastPlayheadSampleTimeUs = 0L;
|
|
this.lastSystemTimeUs = 0L;
|
|
this.previousModeSystemTimeUs = 0L;
|
|
this.notifiedPositionIncreasing = false;
|
|
}
|
|
|
|
public AudioTrackPositionTracker(Listener listener) {
|
|
this.listener = (Listener) Assertions.checkNotNull(listener);
|
|
if (Util.SDK_INT >= 18) {
|
|
try {
|
|
this.getLatencyMethod = AudioTrack.class.getMethod("getLatency", null);
|
|
} catch (NoSuchMethodException unused) {
|
|
}
|
|
}
|
|
this.playheadOffsets = new long[10];
|
|
}
|
|
|
|
public void setAudioTrack(AudioTrack audioTrack, boolean z, int i, int i2, int i3) {
|
|
this.audioTrack = audioTrack;
|
|
this.outputPcmFrameSize = i2;
|
|
this.bufferSize = i3;
|
|
this.audioTimestampPoller = new AudioTimestampPoller(audioTrack);
|
|
this.outputSampleRate = audioTrack.getSampleRate();
|
|
this.needsPassthroughWorkarounds = z && needsPassthroughWorkarounds(i);
|
|
boolean isEncodingLinearPcm = Util.isEncodingLinearPcm(i);
|
|
this.isOutputPcm = isEncodingLinearPcm;
|
|
this.bufferSizeUs = isEncodingLinearPcm ? Util.sampleCountToDurationUs(i3 / i2, this.outputSampleRate) : -9223372036854775807L;
|
|
this.rawPlaybackHeadPosition = 0L;
|
|
this.rawPlaybackHeadWrapCount = 0L;
|
|
this.passthroughWorkaroundPauseOffset = 0L;
|
|
this.hasData = false;
|
|
this.stopTimestampUs = -9223372036854775807L;
|
|
this.forceResetWorkaroundTimeMs = -9223372036854775807L;
|
|
this.lastLatencySampleTimeUs = 0L;
|
|
this.latencyUs = 0L;
|
|
this.audioTrackPlaybackSpeed = 1.0f;
|
|
}
|
|
|
|
public void setAudioTrackPlaybackSpeed(float f) {
|
|
this.audioTrackPlaybackSpeed = f;
|
|
AudioTimestampPoller audioTimestampPoller = this.audioTimestampPoller;
|
|
if (audioTimestampPoller != null) {
|
|
audioTimestampPoller.reset();
|
|
}
|
|
resetSyncParams();
|
|
}
|
|
|
|
public long getCurrentPositionUs(boolean z) {
|
|
long mediaDurationForPlayoutDuration;
|
|
if (((AudioTrack) Assertions.checkNotNull(this.audioTrack)).getPlayState() == 3) {
|
|
maybeSampleSyncParams();
|
|
}
|
|
long nanoTime = System.nanoTime() / 1000;
|
|
AudioTimestampPoller audioTimestampPoller = (AudioTimestampPoller) Assertions.checkNotNull(this.audioTimestampPoller);
|
|
boolean hasAdvancingTimestamp = audioTimestampPoller.hasAdvancingTimestamp();
|
|
if (hasAdvancingTimestamp) {
|
|
mediaDurationForPlayoutDuration = Util.sampleCountToDurationUs(audioTimestampPoller.getTimestampPositionFrames(), this.outputSampleRate) + Util.getMediaDurationForPlayoutDuration(nanoTime - audioTimestampPoller.getTimestampSystemTimeUs(), this.audioTrackPlaybackSpeed);
|
|
} else {
|
|
if (this.playheadOffsetCount == 0) {
|
|
mediaDurationForPlayoutDuration = getPlaybackHeadPositionUs();
|
|
} else {
|
|
mediaDurationForPlayoutDuration = Util.getMediaDurationForPlayoutDuration(this.smoothedPlayheadOffsetUs + nanoTime, this.audioTrackPlaybackSpeed);
|
|
}
|
|
if (!z) {
|
|
mediaDurationForPlayoutDuration = Math.max(0L, mediaDurationForPlayoutDuration - this.latencyUs);
|
|
}
|
|
}
|
|
if (this.lastSampleUsedGetTimestampMode != hasAdvancingTimestamp) {
|
|
this.previousModeSystemTimeUs = this.lastSystemTimeUs;
|
|
this.previousModePositionUs = this.lastPositionUs;
|
|
}
|
|
long j = nanoTime - this.previousModeSystemTimeUs;
|
|
if (j < 1000000) {
|
|
long mediaDurationForPlayoutDuration2 = this.previousModePositionUs + Util.getMediaDurationForPlayoutDuration(j, this.audioTrackPlaybackSpeed);
|
|
long j2 = (j * 1000) / 1000000;
|
|
mediaDurationForPlayoutDuration = ((mediaDurationForPlayoutDuration * j2) + ((1000 - j2) * mediaDurationForPlayoutDuration2)) / 1000;
|
|
}
|
|
if (!this.notifiedPositionIncreasing) {
|
|
long j3 = this.lastPositionUs;
|
|
if (mediaDurationForPlayoutDuration > j3) {
|
|
this.notifiedPositionIncreasing = true;
|
|
this.listener.onPositionAdvancing(System.currentTimeMillis() - Util.usToMs(Util.getPlayoutDurationForMediaDuration(Util.usToMs(mediaDurationForPlayoutDuration - j3), this.audioTrackPlaybackSpeed)));
|
|
}
|
|
}
|
|
this.lastSystemTimeUs = nanoTime;
|
|
this.lastPositionUs = mediaDurationForPlayoutDuration;
|
|
this.lastSampleUsedGetTimestampMode = hasAdvancingTimestamp;
|
|
return mediaDurationForPlayoutDuration;
|
|
}
|
|
|
|
public void start() {
|
|
((AudioTimestampPoller) Assertions.checkNotNull(this.audioTimestampPoller)).reset();
|
|
}
|
|
|
|
public boolean isPlaying() {
|
|
return ((AudioTrack) Assertions.checkNotNull(this.audioTrack)).getPlayState() == 3;
|
|
}
|
|
|
|
public boolean mayHandleBuffer(long j) {
|
|
int playState = ((AudioTrack) Assertions.checkNotNull(this.audioTrack)).getPlayState();
|
|
if (this.needsPassthroughWorkarounds) {
|
|
if (playState == 2) {
|
|
this.hasData = false;
|
|
return false;
|
|
}
|
|
if (playState == 1 && getPlaybackHeadPosition() == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
boolean z = this.hasData;
|
|
boolean hasPendingData = hasPendingData(j);
|
|
this.hasData = hasPendingData;
|
|
if (z && !hasPendingData && playState != 1) {
|
|
this.listener.onUnderrun(this.bufferSize, Util.usToMs(this.bufferSizeUs));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public int getAvailableBufferSize(long j) {
|
|
return this.bufferSize - ((int) (j - (getPlaybackHeadPosition() * this.outputPcmFrameSize)));
|
|
}
|
|
|
|
public boolean isStalled(long j) {
|
|
return this.forceResetWorkaroundTimeMs != -9223372036854775807L && j > 0 && SystemClock.elapsedRealtime() - this.forceResetWorkaroundTimeMs >= FORCE_RESET_WORKAROUND_TIMEOUT_MS;
|
|
}
|
|
|
|
public void handleEndOfStream(long j) {
|
|
this.stopPlaybackHeadPosition = getPlaybackHeadPosition();
|
|
this.stopTimestampUs = SystemClock.elapsedRealtime() * 1000;
|
|
this.endPlaybackHeadPosition = j;
|
|
}
|
|
|
|
public boolean hasPendingData(long j) {
|
|
return j > Util.durationUsToSampleCount(getCurrentPositionUs(false), this.outputSampleRate) || forceHasPendingData();
|
|
}
|
|
|
|
public boolean pause() {
|
|
resetSyncParams();
|
|
if (this.stopTimestampUs != -9223372036854775807L) {
|
|
return false;
|
|
}
|
|
((AudioTimestampPoller) Assertions.checkNotNull(this.audioTimestampPoller)).reset();
|
|
return true;
|
|
}
|
|
|
|
public void reset() {
|
|
resetSyncParams();
|
|
this.audioTrack = null;
|
|
this.audioTimestampPoller = null;
|
|
}
|
|
|
|
private void maybeSampleSyncParams() {
|
|
long nanoTime = System.nanoTime() / 1000;
|
|
if (nanoTime - this.lastPlayheadSampleTimeUs >= 30000) {
|
|
long playbackHeadPositionUs = getPlaybackHeadPositionUs();
|
|
if (playbackHeadPositionUs != 0) {
|
|
this.playheadOffsets[this.nextPlayheadOffsetIndex] = Util.getPlayoutDurationForMediaDuration(playbackHeadPositionUs, this.audioTrackPlaybackSpeed) - nanoTime;
|
|
this.nextPlayheadOffsetIndex = (this.nextPlayheadOffsetIndex + 1) % 10;
|
|
int i = this.playheadOffsetCount;
|
|
if (i < 10) {
|
|
this.playheadOffsetCount = i + 1;
|
|
}
|
|
this.lastPlayheadSampleTimeUs = nanoTime;
|
|
this.smoothedPlayheadOffsetUs = 0L;
|
|
int i2 = 0;
|
|
while (true) {
|
|
int i3 = this.playheadOffsetCount;
|
|
if (i2 >= i3) {
|
|
break;
|
|
}
|
|
this.smoothedPlayheadOffsetUs += this.playheadOffsets[i2] / i3;
|
|
i2++;
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
if (this.needsPassthroughWorkarounds) {
|
|
return;
|
|
}
|
|
maybePollAndCheckTimestamp(nanoTime);
|
|
maybeUpdateLatency(nanoTime);
|
|
}
|
|
|
|
private void maybePollAndCheckTimestamp(long j) {
|
|
AudioTimestampPoller audioTimestampPoller = (AudioTimestampPoller) Assertions.checkNotNull(this.audioTimestampPoller);
|
|
if (audioTimestampPoller.maybePollTimestamp(j)) {
|
|
long timestampSystemTimeUs = audioTimestampPoller.getTimestampSystemTimeUs();
|
|
long timestampPositionFrames = audioTimestampPoller.getTimestampPositionFrames();
|
|
long playbackHeadPositionUs = getPlaybackHeadPositionUs();
|
|
if (Math.abs(timestampSystemTimeUs - j) > 5000000) {
|
|
this.listener.onSystemTimeUsMismatch(timestampPositionFrames, timestampSystemTimeUs, j, playbackHeadPositionUs);
|
|
audioTimestampPoller.rejectTimestamp();
|
|
} else if (Math.abs(Util.sampleCountToDurationUs(timestampPositionFrames, this.outputSampleRate) - playbackHeadPositionUs) > 5000000) {
|
|
this.listener.onPositionFramesMismatch(timestampPositionFrames, timestampSystemTimeUs, j, playbackHeadPositionUs);
|
|
audioTimestampPoller.rejectTimestamp();
|
|
} else {
|
|
audioTimestampPoller.acceptTimestamp();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void maybeUpdateLatency(long j) {
|
|
Method method;
|
|
if (!this.isOutputPcm || (method = this.getLatencyMethod) == null || j - this.lastLatencySampleTimeUs < 500000) {
|
|
return;
|
|
}
|
|
try {
|
|
long intValue = (((Integer) Util.castNonNull((Integer) method.invoke(Assertions.checkNotNull(this.audioTrack), new Object[0]))).intValue() * 1000) - this.bufferSizeUs;
|
|
this.latencyUs = intValue;
|
|
long max = Math.max(intValue, 0L);
|
|
this.latencyUs = max;
|
|
if (max > 5000000) {
|
|
this.listener.onInvalidLatency(max);
|
|
this.latencyUs = 0L;
|
|
}
|
|
} catch (Exception unused) {
|
|
this.getLatencyMethod = null;
|
|
}
|
|
this.lastLatencySampleTimeUs = j;
|
|
}
|
|
|
|
private boolean forceHasPendingData() {
|
|
return this.needsPassthroughWorkarounds && ((AudioTrack) Assertions.checkNotNull(this.audioTrack)).getPlayState() == 2 && getPlaybackHeadPosition() == 0;
|
|
}
|
|
|
|
private static boolean needsPassthroughWorkarounds(int i) {
|
|
return Util.SDK_INT < 23 && (i == 5 || i == 6);
|
|
}
|
|
|
|
private long getPlaybackHeadPositionUs() {
|
|
return Util.sampleCountToDurationUs(getPlaybackHeadPosition(), this.outputSampleRate);
|
|
}
|
|
|
|
private long getPlaybackHeadPosition() {
|
|
long elapsedRealtime = SystemClock.elapsedRealtime();
|
|
long j = this.stopTimestampUs;
|
|
if (j != -9223372036854775807L) {
|
|
return Math.min(this.endPlaybackHeadPosition, this.stopPlaybackHeadPosition + Util.durationUsToSampleCount(Util.getMediaDurationForPlayoutDuration((elapsedRealtime * 1000) - j, this.audioTrackPlaybackSpeed), this.outputSampleRate));
|
|
}
|
|
if (elapsedRealtime - this.lastRawPlaybackHeadPositionSampleTimeMs >= RAW_PLAYBACK_HEAD_POSITION_UPDATE_INTERVAL_MS) {
|
|
updateRawPlaybackHeadPosition(elapsedRealtime);
|
|
this.lastRawPlaybackHeadPositionSampleTimeMs = elapsedRealtime;
|
|
}
|
|
return this.rawPlaybackHeadPosition + (this.rawPlaybackHeadWrapCount << 32);
|
|
}
|
|
|
|
private void updateRawPlaybackHeadPosition(long j) {
|
|
int playState = ((AudioTrack) Assertions.checkNotNull(this.audioTrack)).getPlayState();
|
|
if (playState == 1) {
|
|
return;
|
|
}
|
|
long playbackHeadPosition = r0.getPlaybackHeadPosition() & KeyboardMap.kValueMask;
|
|
if (this.needsPassthroughWorkarounds) {
|
|
if (playState == 2 && playbackHeadPosition == 0) {
|
|
this.passthroughWorkaroundPauseOffset = this.rawPlaybackHeadPosition;
|
|
}
|
|
playbackHeadPosition += this.passthroughWorkaroundPauseOffset;
|
|
}
|
|
if (Util.SDK_INT <= 29) {
|
|
if (playbackHeadPosition == 0 && this.rawPlaybackHeadPosition > 0 && playState == 3) {
|
|
if (this.forceResetWorkaroundTimeMs == -9223372036854775807L) {
|
|
this.forceResetWorkaroundTimeMs = j;
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
this.forceResetWorkaroundTimeMs = -9223372036854775807L;
|
|
}
|
|
if (this.rawPlaybackHeadPosition > playbackHeadPosition) {
|
|
this.rawPlaybackHeadWrapCount++;
|
|
}
|
|
this.rawPlaybackHeadPosition = playbackHeadPosition;
|
|
}
|
|
}
|