Add pixel comparison to FrameExtractorTest

Add PSNR comparison with the output of MedaiMetadataRetriever.

PiperOrigin-RevId: 696190585
This commit is contained in:
dancho 2024-11-13 10:41:26 -08:00 committed by Copybara-Service
parent 74611bbdc0
commit 301ef207f2
6 changed files with 49 additions and 20 deletions

View File

@ -17,6 +17,8 @@ package androidx.media3.transformer;
import static androidx.media3.common.PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND;
import static androidx.media3.test.utils.BitmapPixelTestUtil.maybeSaveTestBitmap;
import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
import static androidx.media3.test.utils.TestUtil.assertBitmapsAreSimilar;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -33,9 +35,13 @@ import androidx.media3.transformer.ExperimentalFrameExtractor.Frame;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@ -49,9 +55,15 @@ import org.junit.runner.RunWith;
/** End-to-end instrumentation test for {@link ExperimentalFrameExtractor}. */
@RunWith(AndroidJUnit4.class)
public class FrameExtractorTest {
// Golden files are generated by MediaMetadataRetriever running on Pixel 8.
private static final String GOLDEN_ASSET_FOLDER_PATH =
"test-generated-goldens/FrameExtractorTest/";
private static final String FILE_PATH =
"asset:///media/mp4/sample_with_increasing_timestamps_360p.mp4";
private static final long TIMEOUT_SECONDS = 10;
// TODO: b/350498258 - Due to bugs in hardware decoders, we can only assert for low PSNR values.
// Move to using software decoders in pixel tests, and increase PSNR threshold.
private static final float PSNR_THRESHOLD = 25f;
@Rule public final TestName testName = new TestName();
@ -77,15 +89,16 @@ public class FrameExtractorTest {
frameExtractor = new ExperimentalFrameExtractor(context, MediaItem.fromUri(FILE_PATH));
ListenableFuture<Frame> frameFuture = frameExtractor.getFrame(/* positionMs= */ 8_500);
Bitmap bitmap = frameFuture.get(TIMEOUT_SECONDS, SECONDS).bitmap;
Frame frame = frameFuture.get(TIMEOUT_SECONDS, SECONDS);
Bitmap actualBitmap = frame.bitmap;
Bitmap expectedBitmap =
readBitmap(
/* assetString= */ GOLDEN_ASSET_FOLDER_PATH
+ "sample_with_increasing_timestamps_360p_8.531.png");
maybeSaveTestBitmap(testId, /* bitmapLabel= */ "actual", actualBitmap, /* path= */ null);
maybeSaveTestBitmap(testId, /* bitmapLabel= */ "actual", bitmap, /* path= */ null);
assertThat(frameFuture.get(TIMEOUT_SECONDS, SECONDS).presentationTimeMs).isEqualTo(8_531);
// TODO: b/350498258 - Actually check Bitmap contents. Due to bugs in hardware decoders,
// such a test would require a too high tolerance.
assertThat(bitmap.getWidth()).isEqualTo(640);
assertThat(bitmap.getHeight()).isEqualTo(360);
assertThat(bitmap.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888);
assertThat(frame.presentationTimeMs).isEqualTo(8_531);
assertBitmapsAreSimilar(expectedBitmap, actualBitmap, PSNR_THRESHOLD);
}
@Test
@ -93,27 +106,43 @@ public class FrameExtractorTest {
frameExtractor = new ExperimentalFrameExtractor(context, MediaItem.fromUri(FILE_PATH));
ListenableFuture<Frame> frameFuture = frameExtractor.getFrame(/* positionMs= */ 200_000);
Frame frame = frameFuture.get(TIMEOUT_SECONDS, SECONDS);
Bitmap actualBitmap = frame.bitmap;
int lastVideoFramePresentationTimeMs = 17_029;
Bitmap expectedBitmap =
readBitmap(
/* assetString= */ GOLDEN_ASSET_FOLDER_PATH
+ "sample_with_increasing_timestamps_360p_17.029.png");
maybeSaveTestBitmap(testId, /* bitmapLabel= */ "actual", actualBitmap, /* path= */ null);
assertThat(frameFuture.get(TIMEOUT_SECONDS, SECONDS).presentationTimeMs)
.isEqualTo(lastVideoFramePresentationTimeMs);
assertThat(frame.presentationTimeMs).isEqualTo(lastVideoFramePresentationTimeMs);
assertBitmapsAreSimilar(expectedBitmap, actualBitmap, PSNR_THRESHOLD);
}
@Test
public void extractFrame_repeatedPositionMs_returnsTheSameFrame() throws Exception {
frameExtractor = new ExperimentalFrameExtractor(context, MediaItem.fromUri(FILE_PATH));
ImmutableList<Long> requestedFramePositionsMs = ImmutableList.of(0L, 0L, 33L, 34L, 34L);
ImmutableList<Long> expectedFramePositionsMs = ImmutableList.of(0L, 0L, 33L, 66L, 66L);
List<ListenableFuture<Frame>> frameFutures = new ArrayList<>();
ListenableFuture<Frame> frame0 = frameExtractor.getFrame(/* positionMs= */ 0);
ListenableFuture<Frame> frame0Again = frameExtractor.getFrame(/* positionMs= */ 0);
ListenableFuture<Frame> frame33 = frameExtractor.getFrame(/* positionMs= */ 33);
ListenableFuture<Frame> frame34 = frameExtractor.getFrame(/* positionMs= */ 34);
ListenableFuture<Frame> frame34Again = frameExtractor.getFrame(/* positionMs= */ 34);
for (long positionMs : requestedFramePositionsMs) {
frameFutures.add(frameExtractor.getFrame(positionMs));
}
for (int i = 0; i < expectedFramePositionsMs.size(); i++) {
ListenableFuture<Frame> frameListenableFuture = frameFutures.get(i);
Frame frame = frameListenableFuture.get(TIMEOUT_SECONDS, SECONDS);
maybeSaveTestBitmap(testId, /* bitmapLabel= */ "actual_" + i, frame.bitmap, /* path= */ null);
Bitmap expectedBitmap =
readBitmap(
/* assetString= */ GOLDEN_ASSET_FOLDER_PATH
+ "sample_with_increasing_timestamps_360p_"
+ String.format(Locale.US, "%.3f", frame.presentationTimeMs / 1000f)
+ ".png");
assertThat(frame0.get(TIMEOUT_SECONDS, SECONDS).presentationTimeMs).isEqualTo(0);
assertThat(frame0Again.get(TIMEOUT_SECONDS, SECONDS).presentationTimeMs).isEqualTo(0);
assertThat(frame33.get(TIMEOUT_SECONDS, SECONDS).presentationTimeMs).isEqualTo(33);
assertThat(frame34.get(TIMEOUT_SECONDS, SECONDS).presentationTimeMs).isEqualTo(66);
assertThat(frame34Again.get(TIMEOUT_SECONDS, SECONDS).presentationTimeMs).isEqualTo(66);
assertBitmapsAreSimilar(expectedBitmap, frame.bitmap, PSNR_THRESHOLD);
assertThat(frame.presentationTimeMs).isEqualTo(expectedFramePositionsMs.get(i));
}
}
@Test