Converging BlankFrameProducer

`BlankFrameProducer` was originally internal to
`DefaultVideoFrameProcessorVideoFrameRenderingTest`. We later copied it in
c221958889. This CL converges the usages.

PiperOrigin-RevId: 555953620
This commit is contained in:
claincly 2023-08-11 15:01:38 +00:00 committed by Tianyi Feng
parent b24f7678bb
commit 2980c30ace

View file

@ -25,25 +25,21 @@ import android.graphics.PixelFormat;
import android.media.Image;
import android.media.ImageReader;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.DebugViewProvider;
import androidx.media3.common.FrameInfo;
import androidx.media3.common.GlObjectsProvider;
import androidx.media3.common.GlTextureInfo;
import androidx.media3.common.SurfaceInfo;
import androidx.media3.common.VideoFrameProcessingException;
import androidx.media3.common.VideoFrameProcessor;
import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.NullableType;
import androidx.media3.common.util.Util;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Longs;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@ -88,7 +84,7 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
long originalPresentationTimeUs = 1234;
AtomicLong actualPresentationTimeUs = new AtomicLong();
processFramesToEndOfStream(
/* inputPresentationTimesUs= */ new long[] {originalPresentationTimeUs},
/* inputPresentationTimesUs= */ ImmutableList.of(originalPresentationTimeUs),
/* onFrameAvailableListener= */ actualPresentationTimeUs::set,
/* renderFramesAutomatically= */ true);
@ -100,7 +96,7 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
@Test
public void automaticFrameRendering_withThreeFrames_reusesInputTimestamps() throws Exception {
long[] originalPresentationTimesUs = new long[] {1234, 3456, 4567};
ImmutableList<Long> originalPresentationTimesUs = ImmutableList.of(1234L, 3456L, 4567L);
ArrayList<Long> actualPresentationTimesUs = new ArrayList<>();
processFramesToEndOfStream(
originalPresentationTimesUs,
@ -119,18 +115,14 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
/* renderFramesAutomatically= */ true);
assertThat(actualPresentationTimesUs)
.containsExactly(
originalPresentationTimesUs[0],
originalPresentationTimesUs[1],
originalPresentationTimesUs[2])
.containsExactlyElementsIn(originalPresentationTimesUs)
.inOrder();
ImmutableList<Long> actualRenderTimesNs =
waitForFrameRenderingAndGetRenderTimesNs(/* expectedFrameCount= */ 3);
assertThat(actualRenderTimesNs)
.containsExactly(
MICROS_TO_NANOS * originalPresentationTimesUs[0],
MICROS_TO_NANOS * originalPresentationTimesUs[1],
MICROS_TO_NANOS * originalPresentationTimesUs[2])
.containsExactlyElementsIn(
// Convert microseconds to nanoseconds.
Iterables.transform(originalPresentationTimesUs, input -> MICROS_TO_NANOS * input))
.inOrder();
}
@ -140,7 +132,7 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
long renderTimesNs = System.nanoTime() + 345678;
AtomicLong actualPresentationTimeUs = new AtomicLong();
processFramesToEndOfStream(
/* inputPresentationTimesUs= */ new long[] {originalPresentationTimeUs},
/* inputPresentationTimesUs= */ ImmutableList.of(originalPresentationTimeUs),
/* onFrameAvailableListener= */ presentationTimeUs -> {
actualPresentationTimeUs.set(presentationTimeUs);
checkNotNull(defaultVideoFrameProcessor).renderOutputFrame(renderTimesNs);
@ -159,7 +151,7 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
long renderTimesNs = VideoFrameProcessor.RENDER_OUTPUT_FRAME_IMMEDIATELY;
AtomicLong actualPresentationTimeUs = new AtomicLong();
processFramesToEndOfStream(
/* inputPresentationTimesUs= */ new long[] {originalPresentationTimeUs},
/* inputPresentationTimesUs= */ ImmutableList.of(originalPresentationTimeUs),
/* onFrameAvailableListener= */ presentationTimeUs -> {
actualPresentationTimeUs.set(presentationTimeUs);
checkNotNull(defaultVideoFrameProcessor).renderOutputFrame(renderTimesNs);
@ -179,7 +171,7 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
long renderTimeBeforeCurrentTimeNs = System.nanoTime() - 345678;
AtomicLong actualPresentationTimeUs = new AtomicLong();
processFramesToEndOfStream(
/* inputPresentationTimesUs= */ new long[] {originalPresentationTimeUs},
/* inputPresentationTimesUs= */ ImmutableList.of(originalPresentationTimeUs),
/* onFrameAvailableListener= */ presentationTimeUs -> {
actualPresentationTimeUs.set(presentationTimeUs);
checkNotNull(defaultVideoFrameProcessor).renderOutputFrame(renderTimeBeforeCurrentTimeNs);
@ -198,7 +190,7 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
long originalPresentationTimeUs = 1234;
AtomicLong actualPresentationTimeUs = new AtomicLong();
processFramesToEndOfStream(
/* inputPresentationTimesUs= */ new long[] {originalPresentationTimeUs},
/* inputPresentationTimesUs= */ ImmutableList.of(originalPresentationTimeUs),
/* onFrameAvailableListener= */ presentationTimeNs -> {
actualPresentationTimeUs.set(presentationTimeNs);
checkNotNull(defaultVideoFrameProcessor)
@ -212,9 +204,10 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
@Test
public void controlledFrameRendering_withThreeIndividualFrames_usesGivenTimestamps()
throws Exception {
long[] originalPresentationTimesUs = new long[] {1234, 3456, 4567};
ImmutableList<Long> originalPresentationTimesUs = ImmutableList.of(1234L, 3456L, 4567L);
long offsetNs = System.nanoTime();
long[] renderTimesNs = new long[] {offsetNs + 123456, offsetNs + 234567, offsetNs + 345678};
ImmutableList<Long> renderTimesNs =
ImmutableList.of(offsetNs + 123456, offsetNs + 234567, offsetNs + 345678);
ArrayList<Long> actualPresentationTimesUs = new ArrayList<>();
AtomicInteger frameIndex = new AtomicInteger();
processFramesToEndOfStream(
@ -222,7 +215,7 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
/* onFrameAvailableListener= */ presentationTimeUs -> {
actualPresentationTimesUs.add(presentationTimeUs);
checkNotNull(defaultVideoFrameProcessor)
.renderOutputFrame(renderTimesNs[frameIndex.getAndIncrement()]);
.renderOutputFrame(renderTimesNs.get(frameIndex.getAndIncrement()));
try {
// TODO(b/264252759): Investigate output frames being dropped and remove sleep.
// Frames can be dropped silently between EGL and the ImageReader. Sleep after each call
@ -236,24 +229,22 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
/* renderFramesAutomatically= */ false);
assertThat(actualPresentationTimesUs)
.containsExactly(
originalPresentationTimesUs[0],
originalPresentationTimesUs[1],
originalPresentationTimesUs[2])
.containsExactlyElementsIn(originalPresentationTimesUs)
.inOrder();
int actualFrameCount = frameIndex.get();
assertThat(actualFrameCount).isEqualTo(originalPresentationTimesUs.length);
long[] actualRenderTimesNs =
Longs.toArray(waitForFrameRenderingAndGetRenderTimesNs(actualFrameCount));
assertThat(actualRenderTimesNs).isEqualTo(renderTimesNs);
assertThat(actualFrameCount).isEqualTo(originalPresentationTimesUs.size());
ImmutableList<Long> actualRenderTimesNs =
waitForFrameRenderingAndGetRenderTimesNs(actualFrameCount);
assertThat(actualRenderTimesNs).containsExactlyElementsIn(renderTimesNs).inOrder();
}
@Test
public void controlledFrameRendering_withThreeFramesAtOnce_usesGivenTimestamps()
throws Exception {
long[] originalPresentationTimesUs = new long[] {1234, 3456, 4567};
ImmutableList<Long> originalPresentationTimesUs = ImmutableList.of(1234L, 3456L, 4567L);
long offsetNs = System.nanoTime();
long[] renderTimesNs = new long[] {offsetNs + 123456, offsetNs + 234567, offsetNs + 345678};
ImmutableList<Long> renderTimesNs =
ImmutableList.of(offsetNs + 123456, offsetNs + 234567, offsetNs + 345678);
ArrayList<Long> actualPresentationTimesUs = new ArrayList<>();
processFramesToEndOfStream(
/* inputPresentationTimesUs= */ originalPresentationTimesUs,
@ -263,22 +254,19 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
// TODO(b/264252759): Investigate output frames being dropped and remove sleep.
// Frames can be dropped silently between EGL and the ImageReader. Sleep after each call
// to swap buffers, to avoid this behavior.
defaultVideoFrameProcessor.renderOutputFrame(renderTimesNs[0]);
defaultVideoFrameProcessor.renderOutputFrame(renderTimesNs.get(0));
Thread.sleep(PER_FRAME_RENDERING_WAIT_TIME_MS);
defaultVideoFrameProcessor.renderOutputFrame(renderTimesNs[1]);
defaultVideoFrameProcessor.renderOutputFrame(renderTimesNs.get(1));
Thread.sleep(PER_FRAME_RENDERING_WAIT_TIME_MS);
defaultVideoFrameProcessor.renderOutputFrame(renderTimesNs[2]);
defaultVideoFrameProcessor.renderOutputFrame(renderTimesNs.get(2));
Thread.sleep(PER_FRAME_RENDERING_WAIT_TIME_MS);
assertThat(actualPresentationTimesUs)
.containsExactly(
originalPresentationTimesUs[0],
originalPresentationTimesUs[1],
originalPresentationTimesUs[2])
.containsExactlyElementsIn(originalPresentationTimesUs)
.inOrder();
long[] actualRenderTimesNs =
Longs.toArray(waitForFrameRenderingAndGetRenderTimesNs(/* expectedFrameCount= */ 3));
assertThat(actualRenderTimesNs).isEqualTo(renderTimesNs);
ImmutableList<Long> actualRenderTimesNs =
waitForFrameRenderingAndGetRenderTimesNs(/* expectedFrameCount= */ 3);
assertThat(actualRenderTimesNs).containsExactlyElementsIn(renderTimesNs).inOrder();
}
private interface OnOutputFrameAvailableForRenderingListener {
@ -287,13 +275,13 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
@EnsuresNonNull("defaultVideoFrameProcessor")
private void processFramesToEndOfStream(
long[] inputPresentationTimesUs,
List<Long> inputPresentationTimesUs,
OnOutputFrameAvailableForRenderingListener onFrameAvailableListener,
boolean renderFramesAutomatically)
throws Exception {
AtomicReference<@NullableType VideoFrameProcessingException>
videoFrameProcessingExceptionReference = new AtomicReference<>();
BlankFrameProducer blankFrameProducer = new BlankFrameProducer();
BlankFrameProducer blankFrameProducer = new BlankFrameProducer(WIDTH, HEIGHT);
CountDownLatch videoFrameProcessingEndedCountDownLatch = new CountDownLatch(1);
defaultVideoFrameProcessor =
checkNotNull(
@ -314,7 +302,7 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
width,
height,
PixelFormat.RGBA_8888,
/* maxImages= */ inputPresentationTimesUs.length);
/* maxImages= */ inputPresentationTimesUs.size());
checkNotNull(defaultVideoFrameProcessor)
.setOutputSurfaceInfo(
new SurfaceInfo(outputImageReader.getSurface(), width, height));
@ -352,7 +340,7 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
INPUT_TYPE_SURFACE,
/* effects= */ ImmutableList.of((GlEffect) (context, useHdr) -> blankFrameProducer),
new FrameInfo.Builder(WIDTH, HEIGHT).build());
blankFrameProducer.produceBlankFramesAndQueueEndOfStream(inputPresentationTimesUs);
blankFrameProducer.produceBlankFrames(inputPresentationTimesUs);
defaultVideoFrameProcessor.signalEndOfInput();
videoFrameProcessingEndedCountDownLatch.await();
@Nullable
@ -373,67 +361,4 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
assertThat(outputRenderTimesNs).isEmpty();
return listBuilder.build();
}
/** Produces blank frames with the given timestamps. */
private static final class BlankFrameProducer implements GlShaderProgram {
private @MonotonicNonNull GlTextureInfo blankTexture;
private @MonotonicNonNull OutputListener outputListener;
public void configureGlObjects() throws VideoFrameProcessingException {
try {
int texId =
GlUtil.createTexture(WIDTH, HEIGHT, /* useHighPrecisionColorComponents= */ false);
int fboId = GlUtil.createFboForTexture(texId);
blankTexture = new GlTextureInfo(texId, fboId, /* rboId= */ C.INDEX_UNSET, WIDTH, HEIGHT);
GlUtil.focusFramebufferUsingCurrentContext(fboId, WIDTH, HEIGHT);
GlUtil.clearFocusedBuffers();
} catch (GlUtil.GlException e) {
throw new VideoFrameProcessingException(e);
}
}
public void produceBlankFramesAndQueueEndOfStream(long[] presentationTimesUs) {
checkNotNull(outputListener);
for (long presentationTimeUs : presentationTimesUs) {
outputListener.onOutputFrameAvailable(checkNotNull(blankTexture), presentationTimeUs);
}
}
@Override
public void setInputListener(InputListener inputListener) {}
@Override
public void setOutputListener(OutputListener outputListener) {
this.outputListener = outputListener;
}
@Override
public void setErrorListener(Executor executor, ErrorListener errorListener) {}
@Override
public void queueInputFrame(
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
// No input is queued in these tests. The BlankFrameProducer is used to produce frames.
throw new UnsupportedOperationException();
}
@Override
public void releaseOutputFrame(GlTextureInfo outputTexture) {}
@Override
public void signalEndOfCurrentInputStream() {
checkNotNull(outputListener).onCurrentOutputStreamEnded();
}
@Override
public void flush() {
throw new UnsupportedOperationException();
}
@Override
public void release() {
// Do nothing as destroying the OpenGL context destroys the texture.
}
}
}