Effect: Update InternalTextureManager to use double timestamps.

Instead of using a long frameDurationUs with Math.floor, use a double
frameDurationUs with Math.round,

Before, playing an image with 30 fps over 1 second would result in the final
timestamp not being aligned to the expected 1 second timestamp. Over long
periods of time, this can lead to significant timestamp drift. Additionally,
for TimestampWrapper and constrained multi-asset, where TimestampWrapper
begins the 2nd effect on the 2nd asset's startTimeUs, this floor() behavior
can lead to the first few frames of the 2nd asset using the first asset's
effects, due to timestamps being mismatched.

PiperOrigin-RevId: 516529240
This commit is contained in:
huangdarwin 2023-03-14 15:16:52 +00:00 committed by tonihei
parent 137b40b76f
commit 9beddc2cc4

View file

@ -16,7 +16,7 @@
package androidx.media3.effect;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static java.lang.Math.floor;
import static java.lang.Math.round;
import android.graphics.Bitmap;
import android.opengl.GLES20;
@ -47,7 +47,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private @MonotonicNonNull GlTextureInfo currentGlTextureInfo;
private int downstreamShaderProgramCapacity;
private int framesToQueueForCurrentBitmap;
private long currentPresentationTimeUs;
private double currentPresentationTimeUs;
private boolean inputEnded;
private boolean useHdr;
private boolean outputEnded;
@ -109,8 +109,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if (inputEnded) {
return;
}
int framesToAdd = (int) floor(frameRate * (durationUs / (float) C.MICROS_PER_SECOND));
long frameDurationUs = (long) floor(C.MICROS_PER_SECOND / frameRate);
int framesToAdd = round(frameRate * (durationUs / (float) C.MICROS_PER_SECOND));
double frameDurationUs = C.MICROS_PER_SECOND / frameRate;
pendingBitmaps.add(new BitmapFrameSequenceInfo(bitmap, frameDurationUs, framesToAdd));
maybeQueueToShaderProgram();
@ -153,7 +153,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
framesToQueueForCurrentBitmap--;
downstreamShaderProgramCapacity--;
shaderProgram.queueInputFrame(checkNotNull(currentGlTextureInfo), currentPresentationTimeUs);
shaderProgram.queueInputFrame(
checkNotNull(currentGlTextureInfo), round(currentPresentationTimeUs));
currentPresentationTimeUs += currentBitmapInfo.frameDurationUs;
if (framesToQueueForCurrentBitmap == 0) {
@ -175,10 +176,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Information to generate all the frames associated with a specific {@link Bitmap}. */
private static final class BitmapFrameSequenceInfo {
public final Bitmap bitmap;
public final long frameDurationUs;
public final double frameDurationUs;
public final int numberOfFrames;
public BitmapFrameSequenceInfo(Bitmap bitmap, long frameDurationUs, int numberOfFrames) {
public BitmapFrameSequenceInfo(Bitmap bitmap, double frameDurationUs, int numberOfFrames) {
this.bitmap = bitmap;
this.frameDurationUs = frameDurationUs;
this.numberOfFrames = numberOfFrames;