diff --git a/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java b/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java index 1a8f84fb71..6ef61c2da1 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java @@ -47,7 +47,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; private final Queue pendingBitmaps; private final GlObjectsProvider glObjectsProvider; - private @MonotonicNonNull GainmapShaderProgram gainmapShaderProgram; + private @MonotonicNonNull RepeatingGainmapShaderProgram repeatingGainmapShaderProgram; @Nullable private GlTextureInfo currentSdrGlTextureInfo; private int downstreamShaderProgramCapacity; private boolean currentInputStreamEnded; @@ -71,13 +71,13 @@ import org.checkerframework.checker.nullness.qual.Nullable; /** * {@inheritDoc} * - *

{@link GlShaderProgram} must be a {@link GainmapShaderProgram}. + *

{@link GlShaderProgram} must be a {@link RepeatingGainmapShaderProgram}. */ @Override public void setSamplingGlShaderProgram(GlShaderProgram samplingGlShaderProgram) { - checkState(samplingGlShaderProgram instanceof GainmapShaderProgram); + checkState(samplingGlShaderProgram instanceof RepeatingGainmapShaderProgram); downstreamShaderProgramCapacity = 0; - this.gainmapShaderProgram = (GainmapShaderProgram) samplingGlShaderProgram; + this.repeatingGainmapShaderProgram = (RepeatingGainmapShaderProgram) samplingGlShaderProgram; } @Override @@ -110,7 +110,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; videoFrameProcessingTaskExecutor.submit( () -> { if (pendingBitmaps.isEmpty()) { - checkNotNull(gainmapShaderProgram).signalEndOfCurrentInputStream(); + checkNotNull(repeatingGainmapShaderProgram).signalEndOfCurrentInputStream(); DebugTraceUtil.logEvent( COMPONENT_BITMAP_TEXTURE_MANAGER, EVENT_SIGNAL_EOS, C.TIME_END_OF_SOURCE); } else { @@ -155,7 +155,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; } downstreamShaderProgramCapacity--; - checkNotNull(gainmapShaderProgram) + checkNotNull(repeatingGainmapShaderProgram) .queueInputFrame( glObjectsProvider, checkNotNull(currentSdrGlTextureInfo), currentPresentationTimeUs); DebugTraceUtil.logEvent( @@ -172,7 +172,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; finishedBitmapInfo.bitmap.recycle(); if (pendingBitmaps.isEmpty() && currentInputStreamEnded) { // Only signal end of stream after all pending bitmaps are processed. - checkNotNull(gainmapShaderProgram).signalEndOfCurrentInputStream(); + checkNotNull(repeatingGainmapShaderProgram).signalEndOfCurrentInputStream(); DebugTraceUtil.logEvent( COMPONENT_BITMAP_TEXTURE_MANAGER, EVENT_SIGNAL_EOS, C.TIME_END_OF_SOURCE); currentInputStreamEnded = false; @@ -213,8 +213,9 @@ import org.checkerframework.checker.nullness.qual.Nullable; frameInfo.width, frameInfo.height); if (Util.SDK_INT >= 34 && bitmap.hasGainmap()) { - checkNotNull(gainmapShaderProgram).setGainmap(checkNotNull(bitmap.getGainmap())); + checkNotNull(repeatingGainmapShaderProgram).setGainmap(checkNotNull(bitmap.getGainmap())); } + checkNotNull(repeatingGainmapShaderProgram).signalNewRepeatingFrameSequence(); } catch (GlUtil.GlException e) { throw VideoFrameProcessingException.from(e); } diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java index 2ff5f398ea..15da706830 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java @@ -61,7 +61,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; */ @SuppressWarnings("FunctionalInterfaceClash") // b/228192298 /* package */ final class DefaultShaderProgram extends BaseGlShaderProgram - implements ExternalShaderProgram, GainmapShaderProgram { + implements ExternalShaderProgram, RepeatingGainmapShaderProgram { private static final String VERTEX_SHADER_TRANSFORMATION_PATH = "shaders/vertex_shader_transformation_es2.glsl"; @@ -153,6 +153,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private @MonotonicNonNull Gainmap lastGainmap; private int gainmapTexId; private @C.ColorTransfer int outputColorTransfer; + private boolean shouldRepeatLastFrame; + private boolean isRepeatingFrameDrawn; /** * Creates a new instance. @@ -501,12 +503,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Override public void drawFrame(int inputTexId, long presentationTimeUs) throws VideoFrameProcessingException { - updateCompositeRgbMatrixArray(presentationTimeUs); - updateCompositeTransformationMatrixAndVisiblePolygon(presentationTimeUs); + boolean compositeRgbMatrixArrayChanged = updateCompositeRgbMatrixArray(presentationTimeUs); + boolean compositeTransformationMatrixAndVisiblePolygonChanged = + updateCompositeTransformationMatrixAndVisiblePolygon(presentationTimeUs); + boolean uniformsChanged = + compositeRgbMatrixArrayChanged || compositeTransformationMatrixAndVisiblePolygonChanged; if (visiblePolygon.size() < 3) { return; // Need at least three visible vertices for a triangle. } + if (shouldRepeatLastFrame && !uniformsChanged && isRepeatingFrameDrawn) { + return; + } try { glProgram.use(); setGainmapSamplerAndUniforms(); @@ -524,6 +532,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } catch (GlUtil.GlException e) { throw new VideoFrameProcessingException(e, presentationTimeUs); } + isRepeatingFrameDrawn = true; } @Override @@ -553,6 +562,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; if (lastGainmap != null && GainmapUtil.equals(this.lastGainmap, gainmap)) { return; } + isRepeatingFrameDrawn = false; this.lastGainmap = gainmap; if (gainmapTexId == C.INDEX_UNSET) { gainmapTexId = GlUtil.createTexture(gainmap.getGainmapContents()); @@ -561,6 +571,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } } + @Override + public void signalNewRepeatingFrameSequence() { + // Skipping drawFrame() is only allowed if there's only one possible output texture. + checkState(outputTexturePool.capacity() == 1); + shouldRepeatLastFrame = true; + isRepeatingFrameDrawn = false; + } + + @Override + public boolean shouldClearTextureBuffer() { + return !(isRepeatingFrameDrawn && shouldRepeatLastFrame); + } + /** * Sets the output {@link C.ColorTransfer}. * @@ -581,8 +604,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * Updates {@link #compositeTransformationMatrixArray} and {@link #visiblePolygon} based on the * given frame timestamp. + * + *

Returns whether the transformation matrix or visible polygon has changed. */ - private void updateCompositeTransformationMatrixAndVisiblePolygon(long presentationTimeUs) { + private boolean updateCompositeTransformationMatrixAndVisiblePolygon(long presentationTimeUs) { float[][] matricesAtPresentationTime = new float[matrixTransformations.size()][16]; for (int i = 0; i < matrixTransformations.size(); i++) { matricesAtPresentationTime[i] = @@ -590,7 +615,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } if (!updateMatrixCache(transformationMatrixCache, matricesAtPresentationTime)) { - return; + return false; } // Compute the compositeTransformationMatrix and transform and clip the visiblePolygon for each @@ -616,7 +641,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; MatrixUtils.transformPoints(transformationMatrix, visiblePolygon)); if (visiblePolygon.size() < 3) { // Can ignore remaining matrices as there are not enough vertices left to form a polygon. - return; + return true; } } // Calculate the input frame vertices corresponding to the output frame's visible polygon. @@ -626,17 +651,22 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; compositeTransformationMatrixArray, /* mOffset= */ 0); visiblePolygon = MatrixUtils.transformPoints(tempResultMatrix, visiblePolygon); + return true; } - /** Updates {@link #compositeRgbMatrixArray} based on the given frame timestamp. */ - private void updateCompositeRgbMatrixArray(long presentationTimeUs) { + /** + * Updates {@link #compositeRgbMatrixArray} based on the given frame timestamp. + * + *

Returns whether the {@link #compositeRgbMatrixArray} has changed. + */ + private boolean updateCompositeRgbMatrixArray(long presentationTimeUs) { float[][] matricesCurrTimestamp = new float[rgbMatrices.size()][16]; for (int i = 0; i < rgbMatrices.size(); i++) { matricesCurrTimestamp[i] = rgbMatrices.get(i).getMatrix(presentationTimeUs, useHdr); } if (!updateMatrixCache(rgbMatrixCache, matricesCurrTimestamp)) { - return; + return false; } GlUtil.setToIdentity(compositeRgbMatrixArray); @@ -656,6 +686,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /* destPost= */ 0, /* length= */ tempResultMatrix.length); } + return true; } /** diff --git a/libraries/effect/src/main/java/androidx/media3/effect/RepeatingFrameShaderProgram.java b/libraries/effect/src/main/java/androidx/media3/effect/RepeatingFrameShaderProgram.java new file mode 100644 index 0000000000..664ea4fb6a --- /dev/null +++ b/libraries/effect/src/main/java/androidx/media3/effect/RepeatingFrameShaderProgram.java @@ -0,0 +1,29 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.effect; + +/** Interface for a {@link GlShaderProgram} that can repeat an input frame. */ +/* package */ interface RepeatingFrameShaderProgram extends GlShaderProgram { + + /** + * Signals that the frame contents will change in the next call to {@link + * GlShaderProgram#queueInputFrame}. + * + *

This class can assume that the input frame contents are unchanged until the next call to + * this method. + */ + void signalNewRepeatingFrameSequence(); +} diff --git a/libraries/effect/src/main/java/androidx/media3/effect/RepeatingGainmapShaderProgram.java b/libraries/effect/src/main/java/androidx/media3/effect/RepeatingGainmapShaderProgram.java new file mode 100644 index 0000000000..a6e6e67f98 --- /dev/null +++ b/libraries/effect/src/main/java/androidx/media3/effect/RepeatingGainmapShaderProgram.java @@ -0,0 +1,19 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.effect; + +/* package */ interface RepeatingGainmapShaderProgram + extends RepeatingFrameShaderProgram, GainmapShaderProgram {} diff --git a/libraries/test_data/src/test/assets/test-generated-goldens/transformer_sequence_effect_test/export_image_samplesFromTextureCorrectly_1.png b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_sequence_effect_test/export_image_samplesFromTextureCorrectly_1.png new file mode 100644 index 0000000000..92b339b5f8 Binary files /dev/null and b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_sequence_effect_test/export_image_samplesFromTextureCorrectly_1.png differ diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/SequenceEffectTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/SequenceEffectTestUtil.java index 5a9f44e914..fe9834423c 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/SequenceEffectTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/SequenceEffectTestUtil.java @@ -138,17 +138,17 @@ public final class SequenceEffectTestUtil { } /** - * Asserts that the first frame extracted from the video in filePath matches output in {@link - * #PNG_ASSET_BASE_PATH}/{@code testId}_0.png. + * Asserts that the first {@code frameCount} frames extracted from the video in {@code filePath} + * match the expected output in {@link #PNG_ASSET_BASE_PATH}/{@code testId}_num.png. * *

Also saves the first frame as a bitmap, in case they differ from expected. */ - public static void assertFirstFrameMatchesExpectedPsnrAndSave( - Context context, String testId, String filePath, float psnrThreshold) + public static void assertFramesMatchExpectedPsnrAndSave( + Context context, String testId, String filePath, float psnrThreshold, int frameCount) throws IOException, InterruptedException { - Bitmap firstEncodedFrame = extractBitmapsFromVideo(context, filePath).get(0); - assertBitmapsMatchExpectedPsnrAndSave( - ImmutableList.of(firstEncodedFrame), testId, psnrThreshold); + ImmutableList frames = + extractBitmapsFromVideo(context, filePath).subList(0, frameCount); + assertBitmapsMatchExpectedPsnrAndSave(frames, testId, psnrThreshold); } private static void assertBitmapsMatchExpectedPsnrAndSave( diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerSequenceEffectTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerSequenceEffectTest.java index c14e765890..89c1994c04 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerSequenceEffectTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerSequenceEffectTest.java @@ -43,7 +43,7 @@ import static androidx.media3.transformer.SequenceEffectTestUtil.PSNR_THRESHOLD; import static androidx.media3.transformer.SequenceEffectTestUtil.PSNR_THRESHOLD_HD; import static androidx.media3.transformer.SequenceEffectTestUtil.SINGLE_30_FPS_VIDEO_FRAME_THRESHOLD_MS; import static androidx.media3.transformer.SequenceEffectTestUtil.assertBitmapsMatchExpectedAndSave; -import static androidx.media3.transformer.SequenceEffectTestUtil.assertFirstFrameMatchesExpectedPsnrAndSave; +import static androidx.media3.transformer.SequenceEffectTestUtil.assertFramesMatchExpectedPsnrAndSave; import static androidx.media3.transformer.SequenceEffectTestUtil.clippedVideo; import static androidx.media3.transformer.SequenceEffectTestUtil.createComposition; import static androidx.media3.transformer.SequenceEffectTestUtil.decoderProducesWashedOutColours; @@ -175,8 +175,8 @@ public final class TransformerSequenceEffectTest { atLeastOneDecoderSucceeds = true; assertThat(new File(result.filePath).length()).isGreaterThan(0); - assertFirstFrameMatchesExpectedPsnrAndSave( - context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD_HD); + assertFramesMatchExpectedPsnrAndSave( + context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD_HD, /* frameCount= */ 1); } assertThat(atLeastOneDecoderSucceeds).isTrue(); } @@ -215,8 +215,8 @@ public final class TransformerSequenceEffectTest { atLeastOneDecoderSucceeds = true; assertThat(new File(result.filePath).length()).isGreaterThan(0); - assertFirstFrameMatchesExpectedPsnrAndSave( - context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD_HD); + assertFramesMatchExpectedPsnrAndSave( + context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD_HD, /* frameCount= */ 1); } assertThat(atLeastOneDecoderSucceeds).isTrue(); } @@ -253,8 +253,8 @@ public final class TransformerSequenceEffectTest { atLeastOneDecoderSucceeds = true; assertThat(new File(result.filePath).length()).isGreaterThan(0); - assertFirstFrameMatchesExpectedPsnrAndSave( - context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD); + assertFramesMatchExpectedPsnrAndSave( + context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD, /* frameCount= */ 1); } assertThat(atLeastOneDecoderSucceeds).isTrue(); } @@ -294,8 +294,8 @@ public final class TransformerSequenceEffectTest { atLeastOneDecoderSucceeds = true; assertThat(new File(result.filePath).length()).isGreaterThan(0); - assertFirstFrameMatchesExpectedPsnrAndSave( - context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD_HD); + assertFramesMatchExpectedPsnrAndSave( + context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD_HD, /* frameCount= */ 1); } assertThat(atLeastOneDecoderSucceeds).isTrue(); } @@ -336,8 +336,8 @@ public final class TransformerSequenceEffectTest { atLeastOneDecoderSucceeds = true; assertThat(new File(result.filePath).length()).isGreaterThan(0); - assertFirstFrameMatchesExpectedPsnrAndSave( - context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD); + assertFramesMatchExpectedPsnrAndSave( + context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD, /* frameCount= */ 1); } assertThat(atLeastOneDecoderSucceeds).isTrue(); @@ -390,12 +390,15 @@ public final class TransformerSequenceEffectTest { assertThat(new File(result.filePath).length()).isGreaterThan(0); // The PSNR threshold was chosen based on: - // Pixel 8 with coordinate rounding error during texture sampling, hits PSNR 23.4. With fix -> - // 29.5 - // Realmi C11 with bug fix hits PSNR 29.94 - // rmx3563 -> 28.8 - assertFirstFrameMatchesExpectedPsnrAndSave( - context, testId, checkNotNull(result.filePath), 28.5f); + // Pixel 8 with coordinate rounding error during texture sampling, gets PSNR 23.4. + // After fix -> 29.5 + // rmx3563 with bug fix achieves PSNR 28.8 + assertFramesMatchExpectedPsnrAndSave( + context, + testId, + checkNotNull(result.filePath), + /* psnrThreshold= */ 28.5f, + /* frameCount= */ 2); } @Test diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TranscodeSpeedTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TranscodeSpeedTest.java index 700461839e..44974c2070 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TranscodeSpeedTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TranscodeSpeedTest.java @@ -15,21 +15,30 @@ */ package androidx.media3.transformer.mh; +import static androidx.media3.common.MimeTypes.VIDEO_H264; import static androidx.media3.transformer.AndroidTestUtil.MP4_LONG_ASSET_WITH_INCREASING_TIMESTAMPS_URI_STRING; +import static androidx.media3.transformer.AndroidTestUtil.ULTRA_HDR_URI_STRING; import static androidx.media3.transformer.AndroidTestUtil.assumeFormatsSupported; import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.net.Uri; +import android.os.Build; +import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Util; +import androidx.media3.effect.Presentation; import androidx.media3.transformer.AndroidTestUtil; import androidx.media3.transformer.EditedMediaItem; +import androidx.media3.transformer.Effects; import androidx.media3.transformer.ExportTestResult; import androidx.media3.transformer.Transformer; import androidx.media3.transformer.TransformerAndroidTestRunner; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.base.Ascii; +import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -77,4 +86,53 @@ public class TranscodeSpeedTest { assertThat(result.throughputFps).isAtLeast(20); } + + @Test + public void exportImage_to720p_completesWithHighThroughput() throws Exception { + Context context = ApplicationProvider.getApplicationContext(); + Format outputFormat = + new Format.Builder() + .setSampleMimeType(VIDEO_H264) + .setFrameRate(30.00f) + .setCodecs("avc1.42C028") + .setWidth(1280) + .setHeight(720) + .build(); + assumeFormatsSupported( + context, + testId, + /* inputFormat= */ AndroidTestUtil.MP4_LONG_ASSET_WITH_INCREASING_TIMESTAMPS_FORMAT, + outputFormat); + Transformer transformer = + new Transformer.Builder(context).setVideoMimeType(MimeTypes.VIDEO_H264).build(); + boolean isHighPerformance = Util.SDK_INT >= 31 && Build.SOC_MODEL.startsWith("Tensor"); + if (Util.SDK_INT == 33 && Ascii.toLowerCase(Util.MODEL).contains("pixel 6")) { + // Pixel 6 is usually quick, unless it's on API 33. + isHighPerformance = false; + } + // This test uses ULTRA_HDR_URI_STRING because it's high resolution. + // Ultra HDR gainmap is ignored. + EditedMediaItem editedMediaItem = + new EditedMediaItem.Builder(MediaItem.fromUri(ULTRA_HDR_URI_STRING)) + .setFrameRate(30) + .setDurationUs(isHighPerformance ? 45_000_000 : 15_000_000) + .setEffects( + new Effects( + /* audioProcessors= */ ImmutableList.of(), + /* videoEffects= */ ImmutableList.of( + Presentation.createForWidthAndHeight( + 720, 1280, Presentation.LAYOUT_SCALE_TO_FIT)))) + .build(); + + ExportTestResult result = + new TransformerAndroidTestRunner.Builder(context, transformer) + .build() + .run(testId, editedMediaItem); + + // This test depends on device GPU performance. Sampling high-resolution textures + // is expensive. If an extra shader program runs on each frame, devices with slow GPU + // such as moto e5 play will drop to 5 fps. + // Devices with a fast GPU and encoder will drop under 300 fps. + assertThat(result.throughputFps).isAtLeast(isHighPerformance ? 400 : 20); + } } diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerSequenceEffectTestWithHdr.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerSequenceEffectTestWithHdr.java index 09e5d8c87b..fbaa245a64 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerSequenceEffectTestWithHdr.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerSequenceEffectTestWithHdr.java @@ -31,7 +31,7 @@ import static androidx.media3.transformer.SequenceEffectTestUtil.NO_EFFECT; import static androidx.media3.transformer.SequenceEffectTestUtil.PSNR_THRESHOLD_HD; import static androidx.media3.transformer.SequenceEffectTestUtil.SINGLE_30_FPS_VIDEO_FRAME_THRESHOLD_MS; import static androidx.media3.transformer.SequenceEffectTestUtil.assertBitmapsMatchExpectedAndSave; -import static androidx.media3.transformer.SequenceEffectTestUtil.assertFirstFrameMatchesExpectedPsnrAndSave; +import static androidx.media3.transformer.SequenceEffectTestUtil.assertFramesMatchExpectedPsnrAndSave; import static androidx.media3.transformer.SequenceEffectTestUtil.clippedVideo; import static androidx.media3.transformer.SequenceEffectTestUtil.createComposition; import static androidx.media3.transformer.SequenceEffectTestUtil.tryToExportCompositionWithDecoder; @@ -226,8 +226,8 @@ public final class TransformerSequenceEffectTestWithHdr { atLeastOneDecoderSucceeds = true; assertThat(checkNotNull(result).filePath).isNotNull(); - assertFirstFrameMatchesExpectedPsnrAndSave( - context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD_HD); + assertFramesMatchExpectedPsnrAndSave( + context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD_HD, /* frameCount= */ 1); } assertThat(atLeastOneDecoderSucceeds).isTrue(); } @@ -261,8 +261,8 @@ public final class TransformerSequenceEffectTestWithHdr { atLeastOneDecoderSucceeds = true; assertThat(checkNotNull(result).filePath).isNotNull(); - assertFirstFrameMatchesExpectedPsnrAndSave( - context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD_HD); + assertFramesMatchExpectedPsnrAndSave( + context, testId, checkNotNull(result.filePath), PSNR_THRESHOLD_HD, /* frameCount= */ 1); } assertThat(atLeastOneDecoderSucceeds).isTrue(); }