Test: Generalize Compositor tests for multi-input, and add more tests.

Tests are added for offset streams with gaps in the middle

PiperOrigin-RevId: 553510171
This commit is contained in:
huangdarwin 2023-08-03 17:02:29 +00:00 committed by Tianyi Feng
parent 57f327840b
commit 80d63a32d3
2 changed files with 200 additions and 92 deletions

View File

@ -226,7 +226,7 @@ public final class DefaultVideoCompositor implements VideoCompositor {
return; return;
} }
// Release frames until the secondary stream has 0-2 frames with time <= // Release frames until the secondary stream has 0-2 frames with presentationTimeUs before or at
// nextTimestampToComposite. // nextTimestampToComposite.
@Nullable InputFrameInfo nextPrimaryFrame = primaryInputSource.frameInfos.peek(); @Nullable InputFrameInfo nextPrimaryFrame = primaryInputSource.frameInfos.peek();
long nextTimestampToComposite = long nextTimestampToComposite =

View File

@ -44,6 +44,7 @@ import androidx.media3.test.utils.TextureBitmapReader;
import androidx.media3.test.utils.VideoFrameProcessorTestRunner; import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -82,6 +83,10 @@ public final class DefaultVideoCompositorPixelTest {
private @MonotonicNonNull String testId; private @MonotonicNonNull String testId;
private @MonotonicNonNull VideoCompositorTestRunner compositorTestRunner; private @MonotonicNonNull VideoCompositorTestRunner compositorTestRunner;
private static final ImmutableList<Effect> TWO_INPUT_COMPOSITOR_EFFECTS =
ImmutableList.of(
RgbFilter.createGrayscaleFilter(),
new ScaleAndRotateTransformation.Builder().setRotationDegrees(180).build());
@Before @Before
@EnsuresNonNull("testId") @EnsuresNonNull("testId")
@ -99,18 +104,20 @@ public final class DefaultVideoCompositorPixelTest {
@Test @Test
@RequiresNonNull("testId") @RequiresNonNull("testId")
public void compositeTwoInputs_withOneFrameFromEach_matchesExpectedBitmap() throws Exception { public void compositeTwoInputs_withOneFrameFromEach_matchesExpectedBitmap() throws Exception {
compositorTestRunner = new VideoCompositorTestRunner(testId, useSharedExecutor); compositorTestRunner =
new VideoCompositorTestRunner(testId, useSharedExecutor, TWO_INPUT_COMPOSITOR_EFFECTS);
compositorTestRunner.queueBitmapsToBothInputs(/* durationSec= */ 1); compositorTestRunner.queueBitmapToAllInputs(/* durationSec= */ 1);
compositorTestRunner.endCompositing();
saveAndAssertBitmapMatchesExpected( saveAndAssertBitmapMatchesExpected(
testId, testId,
compositorTestRunner.inputBitmapReader1.getBitmap(), compositorTestRunner.inputBitmapReaders.get(0).getBitmap(),
/* actualBitmapLabel= */ "actualCompositorInputBitmap1", /* actualBitmapLabel= */ "actualCompositorInputBitmap1",
GRAYSCALE_PNG_ASSET_PATH); GRAYSCALE_PNG_ASSET_PATH);
saveAndAssertBitmapMatchesExpected( saveAndAssertBitmapMatchesExpected(
testId, testId,
compositorTestRunner.inputBitmapReader2.getBitmap(), compositorTestRunner.inputBitmapReaders.get(1).getBitmap(),
/* actualBitmapLabel= */ "actualCompositorInputBitmap2", /* actualBitmapLabel= */ "actualCompositorInputBitmap2",
ROTATE180_PNG_ASSET_PATH); ROTATE180_PNG_ASSET_PATH);
compositorTestRunner.saveAndAssertFirstCompositedBitmapMatchesExpected( compositorTestRunner.saveAndAssertFirstCompositedBitmapMatchesExpected(
@ -121,9 +128,11 @@ public final class DefaultVideoCompositorPixelTest {
@RequiresNonNull("testId") @RequiresNonNull("testId")
public void compositeTwoInputs_withFiveFramesFromEach_matchesExpectedTimestamps() public void compositeTwoInputs_withFiveFramesFromEach_matchesExpectedTimestamps()
throws Exception { throws Exception {
compositorTestRunner = new VideoCompositorTestRunner(testId, useSharedExecutor); compositorTestRunner =
new VideoCompositorTestRunner(testId, useSharedExecutor, TWO_INPUT_COMPOSITOR_EFFECTS);
compositorTestRunner.queueBitmapsToBothInputs(/* durationSec= */ 5); compositorTestRunner.queueBitmapToAllInputs(/* durationSec= */ 5);
compositorTestRunner.endCompositing();
ImmutableList<Long> expectedTimestamps = ImmutableList<Long> expectedTimestamps =
ImmutableList.of( ImmutableList.of(
@ -132,10 +141,10 @@ public final class DefaultVideoCompositorPixelTest {
2 * C.MICROS_PER_SECOND, 2 * C.MICROS_PER_SECOND,
3 * C.MICROS_PER_SECOND, 3 * C.MICROS_PER_SECOND,
4 * C.MICROS_PER_SECOND); 4 * C.MICROS_PER_SECOND);
assertThat(compositorTestRunner.inputBitmapReader1.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(0).getOutputTimestamps())
.containsExactlyElementsIn(expectedTimestamps) .containsExactlyElementsIn(expectedTimestamps)
.inOrder(); .inOrder();
assertThat(compositorTestRunner.inputBitmapReader2.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(1).getOutputTimestamps())
.containsExactlyElementsIn(expectedTimestamps) .containsExactlyElementsIn(expectedTimestamps)
.inOrder(); .inOrder();
assertThat(compositorTestRunner.compositedTimestamps) assertThat(compositorTestRunner.compositedTimestamps)
@ -146,29 +155,32 @@ public final class DefaultVideoCompositorPixelTest {
} }
// TODO: b/262694346 - Add tests for: // TODO: b/262694346 - Add tests for:
// * variable frame-rate.
// * checking correct input frames are composited. // * checking correct input frames are composited.
@Test @Test
@RequiresNonNull("testId") @RequiresNonNull("testId")
public void composite_onePrimaryAndFiveSecondaryFrames_matchesExpectedTimestamps() public void composite_onePrimaryAndFiveSecondaryFrames_matchesExpectedTimestamps()
throws Exception { throws Exception {
compositorTestRunner = new VideoCompositorTestRunner(testId, useSharedExecutor); compositorTestRunner =
new VideoCompositorTestRunner(testId, useSharedExecutor, TWO_INPUT_COMPOSITOR_EFFECTS);
compositorTestRunner.queueBitmapsToBothInputs( compositorTestRunner.queueBitmapToInput(
/* durationSec= */ 1, /* secondarySourceFrameRate= */ 5f); /* inputId= */ 0, /* durationSec= */ 5, /* offsetToAddSec= */ 0L, /* frameRate= */ 0.2f);
compositorTestRunner.queueBitmapToInput(
/* inputId= */ 1, /* durationSec= */ 5, /* offsetToAddSec= */ 0L, /* frameRate= */ 1f);
compositorTestRunner.endCompositing();
ImmutableList<Long> primaryTimestamps = ImmutableList.of(0 * C.MICROS_PER_SECOND); ImmutableList<Long> primaryTimestamps = ImmutableList.of(0 * C.MICROS_PER_SECOND);
ImmutableList<Long> secondaryTimestamps = ImmutableList<Long> secondaryTimestamps =
ImmutableList.of( ImmutableList.of(
0 * C.MICROS_PER_SECOND, 0 * C.MICROS_PER_SECOND,
1 * C.MICROS_PER_SECOND / 5, 1 * C.MICROS_PER_SECOND,
2 * C.MICROS_PER_SECOND / 5, 2 * C.MICROS_PER_SECOND,
3 * C.MICROS_PER_SECOND / 5, 3 * C.MICROS_PER_SECOND,
4 * C.MICROS_PER_SECOND / 5); 4 * C.MICROS_PER_SECOND);
assertThat(compositorTestRunner.inputBitmapReader1.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(0).getOutputTimestamps())
.containsExactlyElementsIn(primaryTimestamps) .containsExactlyElementsIn(primaryTimestamps)
.inOrder(); .inOrder();
assertThat(compositorTestRunner.inputBitmapReader2.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(1).getOutputTimestamps())
.containsExactlyElementsIn(secondaryTimestamps) .containsExactlyElementsIn(secondaryTimestamps)
.inOrder(); .inOrder();
assertThat(compositorTestRunner.compositedTimestamps) assertThat(compositorTestRunner.compositedTimestamps)
@ -182,10 +194,14 @@ public final class DefaultVideoCompositorPixelTest {
@RequiresNonNull("testId") @RequiresNonNull("testId")
public void composite_fivePrimaryAndOneSecondaryFrames_matchesExpectedTimestamps() public void composite_fivePrimaryAndOneSecondaryFrames_matchesExpectedTimestamps()
throws Exception { throws Exception {
compositorTestRunner = new VideoCompositorTestRunner(testId, useSharedExecutor); compositorTestRunner =
new VideoCompositorTestRunner(testId, useSharedExecutor, TWO_INPUT_COMPOSITOR_EFFECTS);
compositorTestRunner.queueBitmapsToBothInputs( compositorTestRunner.queueBitmapToInput(
/* durationSec= */ 5, /* secondarySourceFrameRate= */ .2f); /* inputId= */ 0, /* durationSec= */ 5, /* offsetToAddSec= */ 0L, /* frameRate= */ 1f);
compositorTestRunner.queueBitmapToInput(
/* inputId= */ 1, /* durationSec= */ 5, /* offsetToAddSec= */ 0L, /* frameRate= */ 0.2f);
compositorTestRunner.endCompositing();
ImmutableList<Long> primaryTimestamps = ImmutableList<Long> primaryTimestamps =
ImmutableList.of( ImmutableList.of(
@ -195,10 +211,10 @@ public final class DefaultVideoCompositorPixelTest {
3 * C.MICROS_PER_SECOND, 3 * C.MICROS_PER_SECOND,
4 * C.MICROS_PER_SECOND); 4 * C.MICROS_PER_SECOND);
ImmutableList<Long> secondaryTimestamps = ImmutableList.of(0 * C.MICROS_PER_SECOND); ImmutableList<Long> secondaryTimestamps = ImmutableList.of(0 * C.MICROS_PER_SECOND);
assertThat(compositorTestRunner.inputBitmapReader1.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(0).getOutputTimestamps())
.containsExactlyElementsIn(primaryTimestamps) .containsExactlyElementsIn(primaryTimestamps)
.inOrder(); .inOrder();
assertThat(compositorTestRunner.inputBitmapReader2.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(1).getOutputTimestamps())
.containsExactlyElementsIn(secondaryTimestamps) .containsExactlyElementsIn(secondaryTimestamps)
.inOrder(); .inOrder();
assertThat(compositorTestRunner.compositedTimestamps) assertThat(compositorTestRunner.compositedTimestamps)
@ -212,10 +228,14 @@ public final class DefaultVideoCompositorPixelTest {
@RequiresNonNull("testId") @RequiresNonNull("testId")
public void composite_primaryDoubleSecondaryFrameRate_matchesExpectedTimestamps() public void composite_primaryDoubleSecondaryFrameRate_matchesExpectedTimestamps()
throws Exception { throws Exception {
compositorTestRunner = new VideoCompositorTestRunner(testId, useSharedExecutor); compositorTestRunner =
new VideoCompositorTestRunner(testId, useSharedExecutor, TWO_INPUT_COMPOSITOR_EFFECTS);
compositorTestRunner.queueBitmapsToBothInputs( compositorTestRunner.queueBitmapToInput(
/* durationSec= */ 4, /* secondarySourceFrameRate= */ .5f); /* inputId= */ 0, /* durationSec= */ 4, /* offsetToAddSec= */ 0L, /* frameRate= */ 1f);
compositorTestRunner.queueBitmapToInput(
/* inputId= */ 1, /* durationSec= */ 4, /* offsetToAddSec= */ 0L, /* frameRate= */ 0.5f);
compositorTestRunner.endCompositing();
ImmutableList<Long> primaryTimestamps = ImmutableList<Long> primaryTimestamps =
ImmutableList.of( ImmutableList.of(
@ -225,10 +245,10 @@ public final class DefaultVideoCompositorPixelTest {
3 * C.MICROS_PER_SECOND); 3 * C.MICROS_PER_SECOND);
ImmutableList<Long> secondaryTimestamps = ImmutableList<Long> secondaryTimestamps =
ImmutableList.of(0 * C.MICROS_PER_SECOND, 2 * C.MICROS_PER_SECOND); ImmutableList.of(0 * C.MICROS_PER_SECOND, 2 * C.MICROS_PER_SECOND);
assertThat(compositorTestRunner.inputBitmapReader1.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(0).getOutputTimestamps())
.containsExactlyElementsIn(primaryTimestamps) .containsExactlyElementsIn(primaryTimestamps)
.inOrder(); .inOrder();
assertThat(compositorTestRunner.inputBitmapReader2.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(1).getOutputTimestamps())
.containsExactlyElementsIn(secondaryTimestamps) .containsExactlyElementsIn(secondaryTimestamps)
.inOrder(); .inOrder();
assertThat(compositorTestRunner.compositedTimestamps) assertThat(compositorTestRunner.compositedTimestamps)
@ -241,23 +261,101 @@ public final class DefaultVideoCompositorPixelTest {
@Test @Test
@RequiresNonNull("testId") @RequiresNonNull("testId")
public void composite_primaryHalfSecondaryFrameRate_matchesExpectedTimestamps() throws Exception { public void composite_primaryHalfSecondaryFrameRate_matchesExpectedTimestamps() throws Exception {
compositorTestRunner = new VideoCompositorTestRunner(testId, useSharedExecutor); compositorTestRunner =
new VideoCompositorTestRunner(testId, useSharedExecutor, TWO_INPUT_COMPOSITOR_EFFECTS);
compositorTestRunner.queueBitmapsToBothInputs( compositorTestRunner.queueBitmapToInput(
/* durationSec= */ 2, /* secondarySourceFrameRate= */ 2f); /* inputId= */ 0, /* durationSec= */ 4, /* offsetToAddSec= */ 0L, /* frameRate= */ 0.5f);
compositorTestRunner.queueBitmapToInput(
/* inputId= */ 1, /* durationSec= */ 4, /* offsetToAddSec= */ 0L, /* frameRate= */ 1f);
compositorTestRunner.endCompositing();
ImmutableList<Long> primaryTimestamps = ImmutableList<Long> primaryTimestamps =
ImmutableList.of(0 * C.MICROS_PER_SECOND, 1 * C.MICROS_PER_SECOND); ImmutableList.of(0 * C.MICROS_PER_SECOND, 2 * C.MICROS_PER_SECOND);
ImmutableList<Long> secondaryTimestamps = ImmutableList<Long> secondaryTimestamps =
ImmutableList.of( ImmutableList.of(
0 * C.MICROS_PER_SECOND, 0 * C.MICROS_PER_SECOND,
1 * C.MICROS_PER_SECOND / 2, 1 * C.MICROS_PER_SECOND,
2 * C.MICROS_PER_SECOND / 2, 2 * C.MICROS_PER_SECOND,
3 * C.MICROS_PER_SECOND / 2); 3 * C.MICROS_PER_SECOND);
assertThat(compositorTestRunner.inputBitmapReader1.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(0).getOutputTimestamps())
.containsExactlyElementsIn(primaryTimestamps) .containsExactlyElementsIn(primaryTimestamps)
.inOrder(); .inOrder();
assertThat(compositorTestRunner.inputBitmapReader2.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(1).getOutputTimestamps())
.containsExactlyElementsIn(secondaryTimestamps)
.inOrder();
assertThat(compositorTestRunner.compositedTimestamps)
.containsExactlyElementsIn(primaryTimestamps)
.inOrder();
compositorTestRunner.saveAndAssertFirstCompositedBitmapMatchesExpected(
GRAYSCALE_AND_ROTATE180_COMPOSITE_PNG_ASSET_PATH);
}
@Test
@RequiresNonNull("testId")
public void composite_primaryVariableFrameRateWithOffset_matchesExpectedTimestamps()
throws Exception {
compositorTestRunner =
new VideoCompositorTestRunner(testId, useSharedExecutor, TWO_INPUT_COMPOSITOR_EFFECTS);
compositorTestRunner.queueBitmapToInput(
/* inputId= */ 0, /* durationSec= */ 2, /* offsetToAddSec= */ 1L, /* frameRate= */ 0.5f);
compositorTestRunner.queueBitmapToInput(
/* inputId= */ 0, /* durationSec= */ 2, /* offsetToAddSec= */ 3L, /* frameRate= */ 1f);
compositorTestRunner.queueBitmapToInput(
/* inputId= */ 1, /* durationSec= */ 5, /* offsetToAddSec= */ 0L, /* frameRate= */ 1f);
compositorTestRunner.endCompositing();
ImmutableList<Long> primaryTimestamps =
ImmutableList.of(1 * C.MICROS_PER_SECOND, 3 * C.MICROS_PER_SECOND, 4 * C.MICROS_PER_SECOND);
ImmutableList<Long> secondaryTimestamps =
ImmutableList.of(
0 * C.MICROS_PER_SECOND,
1 * C.MICROS_PER_SECOND,
2 * C.MICROS_PER_SECOND,
3 * C.MICROS_PER_SECOND,
4 * C.MICROS_PER_SECOND);
assertThat(compositorTestRunner.inputBitmapReaders.get(0).getOutputTimestamps())
.containsExactlyElementsIn(primaryTimestamps)
.inOrder();
assertThat(compositorTestRunner.inputBitmapReaders.get(1).getOutputTimestamps())
.containsExactlyElementsIn(secondaryTimestamps)
.inOrder();
assertThat(compositorTestRunner.compositedTimestamps)
.containsExactlyElementsIn(primaryTimestamps)
.inOrder();
compositorTestRunner.saveAndAssertFirstCompositedBitmapMatchesExpected(
GRAYSCALE_AND_ROTATE180_COMPOSITE_PNG_ASSET_PATH);
}
@Test
@RequiresNonNull("testId")
public void composite_secondaryVariableFrameRateWithOffset_matchesExpectedTimestamps()
throws Exception {
compositorTestRunner =
new VideoCompositorTestRunner(testId, useSharedExecutor, TWO_INPUT_COMPOSITOR_EFFECTS);
compositorTestRunner.queueBitmapToInput(
/* inputId= */ 0, /* durationSec= */ 5, /* offsetToAddSec= */ 0L, /* frameRate= */ 1f);
compositorTestRunner.queueBitmapToInput(
/* inputId= */ 1, /* durationSec= */ 2, /* offsetToAddSec= */ 1L, /* frameRate= */ 0.5f);
compositorTestRunner.queueBitmapToInput(
/* inputId= */ 1, /* durationSec= */ 2, /* offsetToAddSec= */ 3L, /* frameRate= */ 1f);
compositorTestRunner.endCompositing();
ImmutableList<Long> primaryTimestamps =
ImmutableList.of(
0 * C.MICROS_PER_SECOND,
1 * C.MICROS_PER_SECOND,
2 * C.MICROS_PER_SECOND,
3 * C.MICROS_PER_SECOND,
4 * C.MICROS_PER_SECOND);
ImmutableList<Long> secondaryTimestamps =
ImmutableList.of(1 * C.MICROS_PER_SECOND, 3 * C.MICROS_PER_SECOND, 4 * C.MICROS_PER_SECOND);
assertThat(compositorTestRunner.inputBitmapReaders.get(0).getOutputTimestamps())
.containsExactlyElementsIn(primaryTimestamps)
.inOrder();
assertThat(compositorTestRunner.inputBitmapReaders.get(1).getOutputTimestamps())
.containsExactlyElementsIn(secondaryTimestamps) .containsExactlyElementsIn(secondaryTimestamps)
.inOrder(); .inOrder();
assertThat(compositorTestRunner.compositedTimestamps) assertThat(compositorTestRunner.compositedTimestamps)
@ -271,14 +369,16 @@ public final class DefaultVideoCompositorPixelTest {
@RequiresNonNull("testId") @RequiresNonNull("testId")
public void compositeTwoInputs_withTenFramesFromEach_matchesExpectedFrameCount() public void compositeTwoInputs_withTenFramesFromEach_matchesExpectedFrameCount()
throws Exception { throws Exception {
compositorTestRunner = new VideoCompositorTestRunner(testId, useSharedExecutor); compositorTestRunner =
new VideoCompositorTestRunner(testId, useSharedExecutor, TWO_INPUT_COMPOSITOR_EFFECTS);
int numberOfFramesToQueue = 10; int numberOfFramesToQueue = 10;
compositorTestRunner.queueBitmapsToBothInputs(numberOfFramesToQueue); compositorTestRunner.queueBitmapToAllInputs(/* durationSec= */ numberOfFramesToQueue);
compositorTestRunner.endCompositing();
assertThat(compositorTestRunner.inputBitmapReader1.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(0).getOutputTimestamps())
.hasSize(numberOfFramesToQueue); .hasSize(numberOfFramesToQueue);
assertThat(compositorTestRunner.inputBitmapReader2.getOutputTimestamps()) assertThat(compositorTestRunner.inputBitmapReaders.get(1).getOutputTimestamps())
.hasSize(numberOfFramesToQueue); .hasSize(numberOfFramesToQueue);
assertThat(compositorTestRunner.compositedTimestamps).hasSize(numberOfFramesToQueue); assertThat(compositorTestRunner.compositedTimestamps).hasSize(numberOfFramesToQueue);
compositorTestRunner.saveAndAssertFirstCompositedBitmapMatchesExpected( compositorTestRunner.saveAndAssertFirstCompositedBitmapMatchesExpected(
@ -293,15 +393,11 @@ public final class DefaultVideoCompositorPixelTest {
private static final class VideoCompositorTestRunner { private static final class VideoCompositorTestRunner {
// Compositor tests rely on 2 VideoFrameProcessor instances, plus the compositor. // Compositor tests rely on 2 VideoFrameProcessor instances, plus the compositor.
private static final int COMPOSITOR_TIMEOUT_MS = 2 * VIDEO_FRAME_PROCESSING_WAIT_MS; private static final int COMPOSITOR_TIMEOUT_MS = 2 * VIDEO_FRAME_PROCESSING_WAIT_MS;
private static final Effect ROTATE_180_EFFECT = private static final int COMPOSITOR_INPUT_SIZE = 2;
new ScaleAndRotateTransformation.Builder().setRotationDegrees(180).build();
private static final Effect GRAYSCALE_EFFECT = RgbFilter.createGrayscaleFilter();
public final TextureBitmapReader inputBitmapReader1;
public final TextureBitmapReader inputBitmapReader2;
public final List<Long> compositedTimestamps; public final List<Long> compositedTimestamps;
private final VideoFrameProcessorTestRunner inputVideoFrameProcessorTestRunner1; public final List<TextureBitmapReader> inputBitmapReaders;
private final VideoFrameProcessorTestRunner inputVideoFrameProcessorTestRunner2; private final List<VideoFrameProcessorTestRunner> inputVideoFrameProcessorTestRunners;
private final VideoCompositor videoCompositor; private final VideoCompositor videoCompositor;
private final @Nullable ExecutorService sharedExecutorService; private final @Nullable ExecutorService sharedExecutorService;
private final AtomicReference<VideoFrameProcessingException> compositionException; private final AtomicReference<VideoFrameProcessingException> compositionException;
@ -309,7 +405,18 @@ public final class DefaultVideoCompositorPixelTest {
private final CountDownLatch compositorEnded; private final CountDownLatch compositorEnded;
private final String testId; private final String testId;
public VideoCompositorTestRunner(String testId, boolean useSharedExecutor) /**
* Creates an instance.
*
* @param testId The {@link String} identifier for the test, used to name output files.
* @param useSharedExecutor Whether to use a shared executor for {@link
* VideoFrameProcessorTestRunner} and {@link VideoCompositor} instances.
* @param inputEffects {@link Effect}s to apply for {@link VideoCompositor} input sources. The
* size of this {@link List} is the amount of inputs. One {@link Effect} is used for each
* input.
*/
public VideoCompositorTestRunner(
String testId, boolean useSharedExecutor, List<Effect> inputEffects)
throws GlUtil.GlException, VideoFrameProcessingException { throws GlUtil.GlException, VideoFrameProcessingException {
this.testId = testId; this.testId = testId;
sharedExecutorService = sharedExecutorService =
@ -357,34 +464,34 @@ public final class DefaultVideoCompositorPixelTest {
releaseOutputTextureCallback.release(presentationTimeUs); releaseOutputTextureCallback.release(presentationTimeUs);
}, },
/* textureOutputCapacity= */ 1); /* textureOutputCapacity= */ 1);
inputBitmapReader1 = new TextureBitmapReader(); inputBitmapReaders = new ArrayList<>();
inputVideoFrameProcessorTestRunner1 = inputVideoFrameProcessorTestRunners = new ArrayList<>();
assertThat(inputEffects).hasSize(COMPOSITOR_INPUT_SIZE);
for (int i = 0; i < inputEffects.size(); i++) {
TextureBitmapReader textureBitmapReader = new TextureBitmapReader();
inputBitmapReaders.add(textureBitmapReader);
VideoFrameProcessorTestRunner vfpTestRunner =
createVideoFrameProcessorTestRunnerBuilder( createVideoFrameProcessorTestRunnerBuilder(
testId, testId,
inputBitmapReader1, textureBitmapReader,
videoCompositor, videoCompositor,
sharedExecutorService, sharedExecutorService,
glObjectsProvider) glObjectsProvider)
.setEffects(GRAYSCALE_EFFECT) .setEffects(inputEffects.get(i))
.build();
inputBitmapReader2 = new TextureBitmapReader();
inputVideoFrameProcessorTestRunner2 =
createVideoFrameProcessorTestRunnerBuilder(
testId,
inputBitmapReader2,
videoCompositor,
sharedExecutorService,
glObjectsProvider)
.setEffects(ROTATE_180_EFFECT)
.build(); .build();
inputVideoFrameProcessorTestRunners.add(vfpTestRunner);
}
} }
/** /**
* Queues {@code durationSec} bitmaps, with one bitmap per second, starting from and including * Queues {@code durationSec} bitmaps, with one bitmap per second, starting from and including
* {@code 0} seconds. Both sources have a {@code frameRate} of {@code 1}. * {@code 0} seconds. Sources have a {@code frameRate} of {@code 1}.
*/ */
public void queueBitmapsToBothInputs(int durationSec) throws IOException { public void queueBitmapToAllInputs(int durationSec) throws IOException {
queueBitmapsToBothInputs(durationSec, /* secondarySourceFrameRate= */ 1); for (int i = 0; i < inputVideoFrameProcessorTestRunners.size(); i++) {
queueBitmapToInput(
/* inputId= */ i, durationSec, /* offsetToAddSec= */ 0L, /* frameRate= */ 1f);
}
} }
/** /**
@ -392,24 +499,24 @@ public final class DefaultVideoCompositorPixelTest {
* {@code 0} seconds. The primary source has a {@code frameRate} of {@code 1}, while secondary * {@code 0} seconds. The primary source has a {@code frameRate} of {@code 1}, while secondary
* sources have a {@code frameRate} of {@code secondarySourceFrameRate}. * sources have a {@code frameRate} of {@code secondarySourceFrameRate}.
*/ */
public void queueBitmapsToBothInputs(int durationSec, float secondarySourceFrameRate) public void queueBitmapToInput(
throws IOException { int inputId, int durationSec, long offsetToAddSec, float frameRate) throws IOException {
inputVideoFrameProcessorTestRunner1.queueInputBitmap( inputVideoFrameProcessorTestRunners
.get(inputId)
.queueInputBitmap(
readBitmap(ORIGINAL_PNG_ASSET_PATH), readBitmap(ORIGINAL_PNG_ASSET_PATH),
/* durationUs= */ durationSec * C.MICROS_PER_SECOND, /* durationUs= */ durationSec * C.MICROS_PER_SECOND,
/* offsetToAddUs= */ 0, /* offsetToAddUs= */ offsetToAddSec * C.MICROS_PER_SECOND,
/* frameRate= */ 1); /* frameRate= */ frameRate);
inputVideoFrameProcessorTestRunner2.queueInputBitmap( }
readBitmap(ORIGINAL_PNG_ASSET_PATH),
/* durationUs= */ durationSec * C.MICROS_PER_SECOND,
/* offsetToAddUs= */ 0,
/* frameRate= */ secondarySourceFrameRate);
inputVideoFrameProcessorTestRunner1.signalEndOfInput(); public void endCompositing() {
inputVideoFrameProcessorTestRunner2.signalEndOfInput(); for (int i = 0; i < inputVideoFrameProcessorTestRunners.size(); i++) {
inputVideoFrameProcessorTestRunners.get(i).signalEndOfInput();
inputVideoFrameProcessorTestRunner1.awaitFrameProcessingEnd(COMPOSITOR_TIMEOUT_MS); }
inputVideoFrameProcessorTestRunner2.awaitFrameProcessingEnd(COMPOSITOR_TIMEOUT_MS); for (int i = 0; i < inputVideoFrameProcessorTestRunners.size(); i++) {
inputVideoFrameProcessorTestRunners.get(i).awaitFrameProcessingEnd(COMPOSITOR_TIMEOUT_MS);
}
@Nullable Exception endCompositingException = null; @Nullable Exception endCompositingException = null;
try { try {
if (!compositorEnded.await(COMPOSITOR_TIMEOUT_MS, MILLISECONDS)) { if (!compositorEnded.await(COMPOSITOR_TIMEOUT_MS, MILLISECONDS)) {
@ -434,8 +541,9 @@ public final class DefaultVideoCompositorPixelTest {
} }
public void release() { public void release() {
inputVideoFrameProcessorTestRunner1.release(); for (int i = 0; i < inputVideoFrameProcessorTestRunners.size(); i++) {
inputVideoFrameProcessorTestRunner2.release(); inputVideoFrameProcessorTestRunners.get(i).release();
}
videoCompositor.release(); videoCompositor.release();
if (sharedExecutorService != null) { if (sharedExecutorService != null) {