mirror of
https://github.com/samsonjs/media.git
synced 2026-04-18 13:25:47 +00:00
Fix position jank after pausing and seeking
Issue: #6901 PiperOrigin-RevId: 314418536
This commit is contained in:
parent
fb011e66a6
commit
a818049143
3 changed files with 47 additions and 12 deletions
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
### 2.11.5 (2020-06-03) ###
|
||||
|
||||
* Improve the smoothness of video playback immediately after starting, seeking
|
||||
or resuming a playback
|
||||
([#6901](https://github.com/google/ExoPlayer/issues/6901)).
|
||||
* Add `SilenceMediaSource.Factory` to support tags.
|
||||
* Enable the configuration of `SilenceSkippingAudioProcessor`
|
||||
([#6705](https://github.com/google/ExoPlayer/issues/6705)).
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
*
|
||||
* <p>If {@link #hasTimestamp()} returns {@code true}, call {@link #getTimestampSystemTimeUs()} to
|
||||
* get the system time at which the latest timestamp was sampled and {@link
|
||||
* #getTimestampPositionFrames()} to get its position in frames. If {@link #isTimestampAdvancing()}
|
||||
* #getTimestampPositionFrames()} to get its position in frames. If {@link #hasAdvancingTimestamp()}
|
||||
* returns {@code true}, the caller should assume that the timestamp has been increasing in real
|
||||
* time since it was sampled. Otherwise, it may be stationary.
|
||||
*
|
||||
|
|
@ -68,7 +68,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
private static final int STATE_ERROR = 4;
|
||||
|
||||
/** The polling interval for {@link #STATE_INITIALIZING} and {@link #STATE_TIMESTAMP}. */
|
||||
private static final int FAST_POLL_INTERVAL_US = 5_000;
|
||||
private static final int FAST_POLL_INTERVAL_US = 10_000;
|
||||
/**
|
||||
* The polling interval for {@link #STATE_TIMESTAMP_ADVANCING} and {@link #STATE_NO_TIMESTAMP}.
|
||||
*/
|
||||
|
|
@ -110,7 +110,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
* timestamp is available via {@link #getTimestampSystemTimeUs()} and {@link
|
||||
* #getTimestampPositionFrames()}, and the caller should call {@link #acceptTimestamp()} if the
|
||||
* timestamp was valid, or {@link #rejectTimestamp()} otherwise. The values returned by {@link
|
||||
* #hasTimestamp()} and {@link #isTimestampAdvancing()} may be updated.
|
||||
* #hasTimestamp()} and {@link #hasAdvancingTimestamp()} may be updated.
|
||||
*
|
||||
* @param systemTimeUs The current system time, in microseconds.
|
||||
* @return Whether the timestamp was updated.
|
||||
|
|
@ -200,12 +200,12 @@ import java.lang.annotation.RetentionPolicy;
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns whether the timestamp appears to be advancing. If {@code true}, call {@link
|
||||
* Returns whether this instance has an advancing timestamp. If {@code true}, call {@link
|
||||
* #getTimestampSystemTimeUs()} and {@link #getTimestampSystemTimeUs()} to access the timestamp. A
|
||||
* current position for the track can be extrapolated based on elapsed real time since the system
|
||||
* time at which the timestamp was sampled.
|
||||
*/
|
||||
public boolean isTimestampAdvancing() {
|
||||
public boolean hasAdvancingTimestamp() {
|
||||
return state == STATE_TIMESTAMP_ADVANCING;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ import java.lang.reflect.Method;
|
|||
* <p>This is a fail safe that should not be required on correctly functioning devices.
|
||||
*/
|
||||
private static final long MAX_LATENCY_US = 5 * C.MICROS_PER_SECOND;
|
||||
/** The duration of time used to smooth over an adjustment between position sampling modes. */
|
||||
private static final long MODE_SWITCH_SMOOTHING_DURATION_US = C.MICROS_PER_SECOND;
|
||||
|
||||
private static final long FORCE_RESET_WORKAROUND_TIMEOUT_MS = 200;
|
||||
|
||||
|
|
@ -160,6 +162,15 @@ import java.lang.reflect.Method;
|
|||
private long stopPlaybackHeadPosition;
|
||||
private long endPlaybackHeadPosition;
|
||||
|
||||
// Results from the previous call to getCurrentPositionUs.
|
||||
private long lastPositionUs;
|
||||
private long lastSystemTimeUs;
|
||||
private boolean lastSampleUsedGetTimestampMode;
|
||||
|
||||
// Results from the last call to getCurrentPositionUs that used a different sample mode.
|
||||
private long previousModePositionUs;
|
||||
private long previousModeSystemTimeUs;
|
||||
|
||||
/**
|
||||
* Creates a new audio track position tracker.
|
||||
*
|
||||
|
|
@ -218,18 +229,16 @@ import java.lang.reflect.Method;
|
|||
// If the device supports it, use the playback timestamp from AudioTrack.getTimestamp.
|
||||
// Otherwise, derive a smoothed position by sampling the track's frame position.
|
||||
long systemTimeUs = System.nanoTime() / 1000;
|
||||
long positionUs;
|
||||
AudioTimestampPoller audioTimestampPoller = Assertions.checkNotNull(this.audioTimestampPoller);
|
||||
if (audioTimestampPoller.hasTimestamp()) {
|
||||
boolean useGetTimestampMode = audioTimestampPoller.hasAdvancingTimestamp();
|
||||
if (useGetTimestampMode) {
|
||||
// Calculate the speed-adjusted position using the timestamp (which may be in the future).
|
||||
long timestampPositionFrames = audioTimestampPoller.getTimestampPositionFrames();
|
||||
long timestampPositionUs = framesToDurationUs(timestampPositionFrames);
|
||||
if (!audioTimestampPoller.isTimestampAdvancing()) {
|
||||
return timestampPositionUs;
|
||||
}
|
||||
long elapsedSinceTimestampUs = systemTimeUs - audioTimestampPoller.getTimestampSystemTimeUs();
|
||||
return timestampPositionUs + elapsedSinceTimestampUs;
|
||||
positionUs = timestampPositionUs + elapsedSinceTimestampUs;
|
||||
} else {
|
||||
long positionUs;
|
||||
if (playheadOffsetCount == 0) {
|
||||
// The AudioTrack has started, but we don't have any samples to compute a smoothed position.
|
||||
positionUs = getPlaybackHeadPositionUs();
|
||||
|
|
@ -242,8 +251,29 @@ import java.lang.reflect.Method;
|
|||
if (!sourceEnded) {
|
||||
positionUs = Math.max(0, positionUs - latencyUs);
|
||||
}
|
||||
return positionUs;
|
||||
}
|
||||
|
||||
if (lastSampleUsedGetTimestampMode != useGetTimestampMode) {
|
||||
// We've switched sampling mode.
|
||||
previousModeSystemTimeUs = lastSystemTimeUs;
|
||||
previousModePositionUs = lastPositionUs;
|
||||
}
|
||||
long elapsedSincePreviousModeUs = systemTimeUs - previousModeSystemTimeUs;
|
||||
if (elapsedSincePreviousModeUs < MODE_SWITCH_SMOOTHING_DURATION_US) {
|
||||
// Use a ramp to smooth between the old mode and the new one to avoid introducing a sudden
|
||||
// jump if the two modes disagree.
|
||||
long previousModeProjectedPositionUs = previousModePositionUs + elapsedSincePreviousModeUs;
|
||||
// A ramp consisting of 1000 points distributed over MODE_SWITCH_SMOOTHING_DURATION_US.
|
||||
long rampPoint = (elapsedSincePreviousModeUs * 1000) / MODE_SWITCH_SMOOTHING_DURATION_US;
|
||||
positionUs *= rampPoint;
|
||||
positionUs += (1000 - rampPoint) * previousModeProjectedPositionUs;
|
||||
positionUs /= 1000;
|
||||
}
|
||||
|
||||
lastSystemTimeUs = systemTimeUs;
|
||||
lastPositionUs = positionUs;
|
||||
lastSampleUsedGetTimestampMode = useGetTimestampMode;
|
||||
return positionUs;
|
||||
}
|
||||
|
||||
/** Starts position tracking. Must be called immediately before {@link AudioTrack#play()}. */
|
||||
|
|
@ -458,6 +488,8 @@ import java.lang.reflect.Method;
|
|||
playheadOffsetCount = 0;
|
||||
nextPlayheadOffsetIndex = 0;
|
||||
lastPlayheadSampleTimeUs = 0;
|
||||
lastSystemTimeUs = 0;
|
||||
previousModeSystemTimeUs = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue