Commit 8192bb55 by olly Committed by Oliver Woodman

VideoFrameReleaseTimeHelper: Simplify and add comments

PiperOrigin-RevId: 345198316
parent 69dcad71
......@@ -38,10 +38,20 @@ import com.google.android.exoplayer2.util.Util;
*/
public final class VideoFrameReleaseTimeHelper {
private static final long CHOREOGRAPHER_SAMPLE_DELAY_MILLIS = 500;
private static final long MAX_ALLOWED_DRIFT_NS = 20_000_000;
/** The period between sampling display VSYNC timestamps, in milliseconds. */
private static final long VSYNC_SAMPLE_UPDATE_PERIOD_MS = 500;
/**
* The maximum adjustment that can be made to a frame release timestamp, in nanoseconds, excluding
* the part of the adjustment that aligns frame release timestamps with the display VSYNC.
*/
private static final long MAX_ALLOWED_ADJUSTMENT_NS = 20_000_000;
/**
* If a frame is targeted to a display VSYNC with timestamp {@code vsyncTime}, the adjusted frame
* release timestamp will be calculated as {@code releaseTime = vsyncTime - ((vsyncDuration *
* VSYNC_OFFSET_PERCENTAGE) / 100)}.
*/
private static final long VSYNC_OFFSET_PERCENTAGE = 80;
private static final int MIN_FRAMES_FOR_ADJUSTMENT = 6;
@Nullable private final WindowManager windowManager;
......@@ -56,14 +66,14 @@ public final class VideoFrameReleaseTimeHelper {
private long vsyncOffsetNs;
private boolean haveSync;
private long syncUnadjustedReleaseTimeNs;
private long syncReleaseTimeNs;
private long syncFramePresentationTimeNs;
private long frameCount;
private long pendingLastAdjustedFrameIndex;
private long pendingLastAdjustedFramePresentationTimeNs;
private long pendingLastAdjustedReleaseTimeNs;
private long lastAdjustedFrameIndex;
private long lastAdjustedFramePresentationTimeNs;
private long lastAdjustedReleaseTimeNs;
/**
* Constructs an instance that smooths frame release timestamps but does not align them with
......@@ -159,7 +169,7 @@ public final class VideoFrameReleaseTimeHelper {
*/
public void onNextFrame(long framePresentationTimeUs) {
lastAdjustedFrameIndex = pendingLastAdjustedFrameIndex;
lastAdjustedFramePresentationTimeNs = pendingLastAdjustedFramePresentationTimeNs;
lastAdjustedReleaseTimeNs = pendingLastAdjustedReleaseTimeNs;
nextFramePresentationTimeUs = framePresentationTimeUs;
frameCount++;
}
......@@ -178,17 +188,16 @@ public final class VideoFrameReleaseTimeHelper {
* once (if the caller wishes to give the helper the opportunity to refine a release time closer
* to when the frame needs to be released).
*
* @param unadjustedReleaseTimeNs The frame's unadjusted release time, in nanoseconds and in the
* same time base as {@link System#nanoTime()}.
* @param releaseTimeNs The frame's unadjusted release time, in nanoseconds and in the same time
* base as {@link System#nanoTime()}.
* @return The adjusted frame release timestamp, in nanoseconds and in the same time base as
* {@link System#nanoTime()}.
*/
public long adjustReleaseTime(long unadjustedReleaseTimeNs) {
public long adjustReleaseTime(long releaseTimeNs) {
long framePresentationTimeNs = nextFramePresentationTimeUs * 1000;
// Until we know better, the adjustment will be a no-op.
long adjustedFramePresentationTimeNs = framePresentationTimeNs;
long adjustedReleaseTimeNs = unadjustedReleaseTimeNs;
long adjustedReleaseTimeNs = releaseTimeNs;
if (haveSync) {
if (frameCount >= MIN_FRAMES_FOR_ADJUSTMENT) {
......@@ -199,23 +208,21 @@ public final class VideoFrameReleaseTimeHelper {
long averageFrameDurationNs = (framePresentationTimeNs - syncFramePresentationTimeNs)
/ frameCount;
// Project the adjusted frame time forward using the average.
long candidateAdjustedFramePresentationTimeNs =
lastAdjustedFramePresentationTimeNs
long candidateAdjustedReleaseTimeNs =
lastAdjustedReleaseTimeNs
+ averageFrameDurationNs * (frameCount - lastAdjustedFrameIndex);
if (isDriftTooLarge(candidateAdjustedFramePresentationTimeNs, unadjustedReleaseTimeNs)) {
haveSync = false;
if (adjustmentAllowed(releaseTimeNs, candidateAdjustedReleaseTimeNs)) {
adjustedReleaseTimeNs = candidateAdjustedReleaseTimeNs;
} else {
adjustedFramePresentationTimeNs = candidateAdjustedFramePresentationTimeNs;
adjustedReleaseTimeNs =
syncUnadjustedReleaseTimeNs
+ adjustedFramePresentationTimeNs
- syncFramePresentationTimeNs;
haveSync = false;
}
} else {
// We're synced but haven't waited the required number of frames to apply an adjustment.
// Check drift anyway.
if (isDriftTooLarge(framePresentationTimeNs, unadjustedReleaseTimeNs)) {
// Check for drift between the proposed and projected frame release timestamps.
long projectedReleaseTimeNs =
syncReleaseTimeNs + (framePresentationTimeNs - syncFramePresentationTimeNs);
if (!adjustmentAllowed(releaseTimeNs, projectedReleaseTimeNs)) {
haveSync = false;
}
}
......@@ -224,13 +231,13 @@ public final class VideoFrameReleaseTimeHelper {
// If we need to sync, do so now.
if (!haveSync) {
syncFramePresentationTimeNs = framePresentationTimeNs;
syncUnadjustedReleaseTimeNs = unadjustedReleaseTimeNs;
syncReleaseTimeNs = releaseTimeNs;
frameCount = 0;
haveSync = true;
}
pendingLastAdjustedFrameIndex = frameCount;
pendingLastAdjustedFramePresentationTimeNs = adjustedFramePresentationTimeNs;
pendingLastAdjustedReleaseTimeNs = adjustedReleaseTimeNs;
if (vsyncSampler == null || vsyncDurationNs == C.TIME_UNSET) {
return adjustedReleaseTimeNs;
......@@ -262,10 +269,9 @@ public final class VideoFrameReleaseTimeHelper {
}
}
private boolean isDriftTooLarge(long frameTimeNs, long releaseTimeNs) {
long elapsedFrameTimeNs = frameTimeNs - syncFramePresentationTimeNs;
long elapsedReleaseTimeNs = releaseTimeNs - syncUnadjustedReleaseTimeNs;
return Math.abs(elapsedReleaseTimeNs - elapsedFrameTimeNs) > MAX_ALLOWED_DRIFT_NS;
private static boolean adjustmentAllowed(
long unadjustedReleaseTimeNs, long adjustedReleaseTimeNs) {
return Math.abs(unadjustedReleaseTimeNs - adjustedReleaseTimeNs) <= MAX_ALLOWED_ADJUSTMENT_NS;
}
private static long closestVsync(long releaseTime, long sampledVsyncTime, long vsyncDuration) {
......@@ -372,7 +378,7 @@ public final class VideoFrameReleaseTimeHelper {
@Override
public void doFrame(long vsyncTimeNs) {
sampledVsyncTimeNs = vsyncTimeNs;
choreographer.postFrameCallbackDelayed(this, CHOREOGRAPHER_SAMPLE_DELAY_MILLIS);
choreographer.postFrameCallbackDelayed(this, VSYNC_SAMPLE_UPDATE_PERIOD_MS);
}
@Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment