From 9ced27a0305f7a3abe1db7ce59f2e26836c4d7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Schia?= Date: Sat, 20 Apr 2024 08:16:43 +0200 Subject: [PATCH 1/6] Use inputFormat.tileCountHorizontal to calculate tileStartXCoordinate for cropping the correct tile from outputBitmap To find the column of an index in a matrix the formula "column = index % width" should be used, not "column = index % height" If inputFormat.tileCountVertical was equal to 1 then it would not throw an error, but instead result in the first tile of the bitmap always being returned. If inputFormat.tileCountVertical was larger than 1 then Bitmap.createBitmap() would throw an error as it would attempt to go outside the bounds of outputBitmap ImageRenderTest has been updated to test for 2x3 images so that tileCountVertical != tileCountHorizontal. These tests passed previously because they were equal, so using tileCountVertical produced the same results as tileCountHorizontal --- .../media3/exoplayer/image/ImageRenderer.java | 2 +- .../exoplayer/image/ImageRendererTest.java | 110 +++++++++++------- 2 files changed, 71 insertions(+), 41 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/image/ImageRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/image/ImageRenderer.java index b05da06548..344eb6d2da 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/image/ImageRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/image/ImageRenderer.java @@ -571,7 +571,7 @@ public class ImageRenderer extends BaseRenderer { checkStateNotNull(outputBitmap); int tileWidth = outputBitmap.getWidth() / checkStateNotNull(inputFormat).tileCountHorizontal; int tileHeight = outputBitmap.getHeight() / checkStateNotNull(inputFormat).tileCountVertical; - int tileStartXCoordinate = tileWidth * (tileIndex % inputFormat.tileCountVertical); + int tileStartXCoordinate = tileWidth * (tileIndex % inputFormat.tileCountHorizontal); int tileStartYCoordinate = tileHeight * (tileIndex / inputFormat.tileCountHorizontal); return Bitmap.createBitmap( outputBitmap, tileStartXCoordinate, tileStartYCoordinate, tileWidth, tileHeight); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java index 242f9b4d45..beee713a0a 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java @@ -66,16 +66,16 @@ public class ImageRendererTest { .setTileCountVertical(1) .setTileCountHorizontal(1) .build(); - private static final Format JPEG_FORMAT_WITH_FOUR_TILES = + private static final Format JPEG_FORMAT_WITH_SIX_TILES = new Format.Builder() .setSampleMimeType(MimeTypes.IMAGE_JPEG) - .setTileCountVertical(2) + .setTileCountVertical(3) .setTileCountHorizontal(2) .build(); private final List> renderedBitmaps = new ArrayList<>(); private final Bitmap fakeDecodedBitmap1 = - Bitmap.createBitmap(/* width= */ 2, /* height= */ 2, Bitmap.Config.ARGB_8888); + Bitmap.createBitmap(/* width= */ 2, /* height= */ 3, Bitmap.Config.ARGB_8888); private final Bitmap fakeDecodedBitmap2 = Bitmap.createBitmap(/* width= */ 4, /* height= */ 4, Bitmap.Config.ARGB_8888); @@ -223,16 +223,18 @@ public class ImageRendererTest { throws Exception { FakeSampleStream fakeSampleStream1 = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 300_000L, /* flags= */ 0))); + emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 500_000L, /* flags= */ 0))); fakeSampleStream1.writeData(/* startPositionUs= */ 0); FakeSampleStream fakeSampleStream2 = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 10L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM)); @@ -254,25 +256,27 @@ public class ImageRendererTest { renderer.start(); renderer.render(/* positionUs= */ 200_000L, /* elapsedRealtimeUs= */ 0); renderer.render(/* positionUs= */ 300_000L, /* elapsedRealtimeUs= */ 0); + renderer.render(/* positionUs= */ 400_000L, /* elapsedRealtimeUs= */ 0); + renderer.render(/* positionUs= */ 500_000L, /* elapsedRealtimeUs= */ 0); renderer.replaceStream( new Format[] {PNG_FORMAT}, fakeSampleStream2, /* startPositionUs= */ 10, - /* offsetUs= */ 450_000L, + /* offsetUs= */ 650_000L, new MediaSource.MediaPeriodId(new Object())); renderer.setCurrentStreamFinal(); // Render last sample of first stream - renderer.render(/* positionUs= */ 400_000L, /* elapsedRealtimeUs= */ 0); + renderer.render(/* positionUs= */ 600_000L, /* elapsedRealtimeUs= */ 0); StopWatch hasReadStreamToEndStopWatch = new StopWatch(HAS_READ_STREAM_TO_END_TIMEOUT_MESSAGE); while (!renderer.hasReadStreamToEnd() && hasReadStreamToEndStopWatch.ensureNotExpired()) { - renderer.render(/* positionUs= */ 450_010L, /* elapsedRealtimeUs= */ 0L); + renderer.render(/* positionUs= */ 650_010L, /* elapsedRealtimeUs= */ 0L); } renderer.stop(); - assertThat(renderedBitmaps).hasSize(5); + assertThat(renderedBitmaps).hasSize(7); assertThat(renderedBitmaps.get(0).first).isEqualTo(0); - assertThat(renderedBitmaps.get(4).first).isEqualTo(10L); + assertThat(renderedBitmaps.get(6).first).isEqualTo(10L); } @Test @@ -280,7 +284,7 @@ public class ImageRendererTest { throws Exception { FakeSampleStream fakeSampleStream1 = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), @@ -289,7 +293,7 @@ public class ImageRendererTest { fakeSampleStream1.writeData(/* startPositionUs= */ 0); FakeSampleStream fakeSampleStream2 = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 10L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM)); @@ -397,17 +401,19 @@ public class ImageRendererTest { public void render_tiledImage_cropsAndRendersToImageOutput() throws Exception { FakeSampleStream fakeSampleStream = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), END_OF_STREAM_ITEM)); fakeSampleStream.writeData(/* startPositionUs= */ 0); renderer.enable( RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, fakeSampleStream, /* positionUs= */ 0, /* joining= */ false, @@ -431,7 +437,7 @@ public class ImageRendererTest { positionUs += 100_000; } - assertThat(renderedBitmaps).hasSize(4); + assertThat(renderedBitmaps).hasSize(6); assertThat(renderedBitmaps.get(0).first).isEqualTo(0L); assertThat(renderedBitmaps.get(0).second.getHeight()).isEqualTo(1); assertThat(renderedBitmaps.get(0).second.getWidth()).isEqualTo(1); @@ -444,23 +450,31 @@ public class ImageRendererTest { assertThat(renderedBitmaps.get(3).first).isEqualTo(300_000L); assertThat(renderedBitmaps.get(3).second.getHeight()).isEqualTo(1); assertThat(renderedBitmaps.get(3).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(4).first).isEqualTo(400_000L); + assertThat(renderedBitmaps.get(4).second.getHeight()).isEqualTo(1); + assertThat(renderedBitmaps.get(4).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(5).first).isEqualTo(500_000L); + assertThat(renderedBitmaps.get(5).second.getHeight()).isEqualTo(1); + assertThat(renderedBitmaps.get(5).second.getWidth()).isEqualTo(1); } @Test public void render_tiledImageWithNonZeroStartPosition_rendersToImageOutput() throws Exception { FakeSampleStream fakeSampleStream = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), END_OF_STREAM_ITEM)); fakeSampleStream.writeData(/* startPositionUs= */ 0); renderer.enable( RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, fakeSampleStream, /* positionUs= */ 200_000, /* joining= */ false, @@ -484,9 +498,11 @@ public class ImageRendererTest { positionUs += 100_000; } - assertThat(renderedBitmaps).hasSize(2); + assertThat(renderedBitmaps).hasSize(4); assertThat(renderedBitmaps.get(0).first).isEqualTo(200_000L); assertThat(renderedBitmaps.get(1).first).isEqualTo(300_000L); + assertThat(renderedBitmaps.get(2).first).isEqualTo(400_000L); + assertThat(renderedBitmaps.get(3).first).isEqualTo(500_000L); } @Test @@ -494,17 +510,19 @@ public class ImageRendererTest { throws Exception { FakeSampleStream fakeSampleStream = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), END_OF_STREAM_ITEM)); fakeSampleStream.writeData(/* startPositionUs= */ 0); renderer.enable( RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, fakeSampleStream, /* positionUs= */ 0, /* joining= */ false, @@ -517,11 +535,11 @@ public class ImageRendererTest { StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { renderer.render( - /* positionUs= */ 350_000, + /* positionUs= */ 550_000, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); } StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); - long positionUs = 350_000; + long positionUs = 550_000; while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { renderer.render( positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); @@ -529,7 +547,7 @@ public class ImageRendererTest { } assertThat(renderedBitmaps).hasSize(1); - assertThat(renderedBitmaps.get(0).first).isEqualTo(300_000L); + assertThat(renderedBitmaps.get(0).first).isEqualTo(500_000L); } @Test @@ -537,17 +555,19 @@ public class ImageRendererTest { throws Exception { FakeSampleStream fakeSampleStream = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), END_OF_STREAM_ITEM)); fakeSampleStream.writeData(/* startPositionUs= */ 0); renderer.enable( RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, fakeSampleStream, /* positionUs= */ 0, /* joining= */ false, @@ -560,11 +580,11 @@ public class ImageRendererTest { StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { renderer.render( - /* positionUs= */ 250_000L, + /* positionUs= */ 450_000L, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); } StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); - long positionUs = 250_000L; + long positionUs = 450_000L; while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { renderer.render( positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); @@ -572,8 +592,8 @@ public class ImageRendererTest { } assertThat(renderedBitmaps).hasSize(2); - assertThat(renderedBitmaps.get(0).first).isEqualTo(200_000L); - assertThat(renderedBitmaps.get(1).first).isEqualTo(300_000L); + assertThat(renderedBitmaps.get(0).first).isEqualTo(400_000L); + assertThat(renderedBitmaps.get(1).first).isEqualTo(500_000L); } @Test @@ -582,17 +602,19 @@ public class ImageRendererTest { throws Exception { FakeSampleStream fakeSampleStream = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), END_OF_STREAM_ITEM)); fakeSampleStream.writeData(/* startPositionUs= */ 0); renderer.enable( RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, fakeSampleStream, /* positionUs= */ 0, /* joining= */ false, @@ -616,10 +638,12 @@ public class ImageRendererTest { positionUs += 100_000; } - assertThat(renderedBitmaps).hasSize(3); + assertThat(renderedBitmaps).hasSize(5); assertThat(renderedBitmaps.get(0).first).isEqualTo(100_000L); assertThat(renderedBitmaps.get(1).first).isEqualTo(200_000L); assertThat(renderedBitmaps.get(2).first).isEqualTo(300_000L); + assertThat(renderedBitmaps.get(3).first).isEqualTo(400_000L); + assertThat(renderedBitmaps.get(4).first).isEqualTo(500_000L); } @Test @@ -628,17 +652,19 @@ public class ImageRendererTest { throws Exception { FakeSampleStream fakeSampleStream = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), END_OF_STREAM_ITEM)); fakeSampleStream.writeData(/* startPositionUs= */ 0); renderer.enable( RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, fakeSampleStream, /* positionUs= */ 0, /* joining= */ false, @@ -662,10 +688,12 @@ public class ImageRendererTest { positionUs += 100_000; } - assertThat(renderedBitmaps).hasSize(3); + assertThat(renderedBitmaps).hasSize(5); assertThat(renderedBitmaps.get(0).first).isEqualTo(100_000L); assertThat(renderedBitmaps.get(1).first).isEqualTo(200_000L); assertThat(renderedBitmaps.get(2).first).isEqualTo(300_000L); + assertThat(renderedBitmaps.get(3).first).isEqualTo(400_000L); + assertThat(renderedBitmaps.get(4).first).isEqualTo(500_000L); } @Test @@ -673,17 +701,19 @@ public class ImageRendererTest { throws Exception { FakeSampleStream fakeSampleStream = createSampleStream( - JPEG_FORMAT_WITH_FOUR_TILES, + JPEG_FORMAT_WITH_SIX_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), END_OF_STREAM_ITEM)); fakeSampleStream.writeData(/* startPositionUs= */ 0); renderer.enable( RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, fakeSampleStream, /* positionUs= */ 0, /* joining= */ false, @@ -696,7 +726,7 @@ public class ImageRendererTest { StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { renderer.render( - /* positionUs= */ 330_000, + /* positionUs= */ 530_000, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); } StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); @@ -708,7 +738,7 @@ public class ImageRendererTest { } assertThat(renderedBitmaps).hasSize(1); - assertThat(renderedBitmaps.get(0).first).isEqualTo(300_000L); + assertThat(renderedBitmaps.get(0).first).isEqualTo(500_000L); } private static FakeSampleStream.FakeSampleStreamItem emptySample( From e27f527ed1092da863df0c73206623b6007955ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Schia?= Date: Fri, 26 Apr 2024 18:36:36 +0200 Subject: [PATCH 2/6] Revert changes in ImageRenderTest from 3a030da83b49b9f6c0e414955b543433221d4c41 and create a new unit test for non square images instead --- .../exoplayer/image/ImageRendererTest.java | 645 ++++++++++-------- 1 file changed, 346 insertions(+), 299 deletions(-) diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java index beee713a0a..badac24f98 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java @@ -66,6 +66,12 @@ public class ImageRendererTest { .setTileCountVertical(1) .setTileCountHorizontal(1) .build(); + private static final Format JPEG_FORMAT_WITH_FOUR_TILES = + new Format.Builder() + .setSampleMimeType(MimeTypes.IMAGE_JPEG) + .setTileCountVertical(2) + .setTileCountHorizontal(2) + .build(); private static final Format JPEG_FORMAT_WITH_SIX_TILES = new Format.Builder() .setSampleMimeType(MimeTypes.IMAGE_JPEG) @@ -75,19 +81,28 @@ public class ImageRendererTest { private final List> renderedBitmaps = new ArrayList<>(); private final Bitmap fakeDecodedBitmap1 = - Bitmap.createBitmap(/* width= */ 2, /* height= */ 3, Bitmap.Config.ARGB_8888); + Bitmap.createBitmap(/* width= */ 2, /* height= */ 2, Bitmap.Config.ARGB_8888); private final Bitmap fakeDecodedBitmap2 = Bitmap.createBitmap(/* width= */ 4, /* height= */ 4, Bitmap.Config.ARGB_8888); + private final Bitmap fakeDecodedBitmap3 = + Bitmap.createBitmap(/* width= */ 2, /* height= */ 3, Bitmap.Config.ARGB_8888); private ImageRenderer renderer; private int decodeCallCount; + private Bitmap overridenBitmap = null; @Before public void setUp() throws Exception { decodeCallCount = 0; ImageDecoder.Factory fakeDecoderFactory = new BitmapFactoryImageDecoder.Factory( - (data, length) -> ++decodeCallCount == 1 ? fakeDecodedBitmap1 : fakeDecodedBitmap2); + (data, length) -> { + if (overridenBitmap != null) { + return overridenBitmap; + } else { + return ++decodeCallCount == 1 ? fakeDecodedBitmap1 : fakeDecodedBitmap2; + } + }); ImageOutput queuingImageOutput = new ImageOutput() { @Override @@ -223,18 +238,16 @@ public class ImageRendererTest { throws Exception { FakeSampleStream fakeSampleStream1 = createSampleStream( - JPEG_FORMAT_WITH_SIX_TILES, + JPEG_FORMAT_WITH_FOUR_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 500_000L, /* flags= */ 0))); + emptySample(/* timeUs= */ 300_000L, /* flags= */ 0))); fakeSampleStream1.writeData(/* startPositionUs= */ 0); FakeSampleStream fakeSampleStream2 = createSampleStream( - JPEG_FORMAT_WITH_SIX_TILES, + JPEG_FORMAT_WITH_FOUR_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 10L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM)); @@ -256,27 +269,25 @@ public class ImageRendererTest { renderer.start(); renderer.render(/* positionUs= */ 200_000L, /* elapsedRealtimeUs= */ 0); renderer.render(/* positionUs= */ 300_000L, /* elapsedRealtimeUs= */ 0); - renderer.render(/* positionUs= */ 400_000L, /* elapsedRealtimeUs= */ 0); - renderer.render(/* positionUs= */ 500_000L, /* elapsedRealtimeUs= */ 0); renderer.replaceStream( new Format[] {PNG_FORMAT}, fakeSampleStream2, /* startPositionUs= */ 10, - /* offsetUs= */ 650_000L, + /* offsetUs= */ 450_000L, new MediaSource.MediaPeriodId(new Object())); renderer.setCurrentStreamFinal(); // Render last sample of first stream - renderer.render(/* positionUs= */ 600_000L, /* elapsedRealtimeUs= */ 0); + renderer.render(/* positionUs= */ 400_000L, /* elapsedRealtimeUs= */ 0); StopWatch hasReadStreamToEndStopWatch = new StopWatch(HAS_READ_STREAM_TO_END_TIMEOUT_MESSAGE); while (!renderer.hasReadStreamToEnd() && hasReadStreamToEndStopWatch.ensureNotExpired()) { - renderer.render(/* positionUs= */ 650_010L, /* elapsedRealtimeUs= */ 0L); + renderer.render(/* positionUs= */ 450_010L, /* elapsedRealtimeUs= */ 0L); } renderer.stop(); - assertThat(renderedBitmaps).hasSize(7); + assertThat(renderedBitmaps).hasSize(5); assertThat(renderedBitmaps.get(0).first).isEqualTo(0); - assertThat(renderedBitmaps.get(6).first).isEqualTo(10L); + assertThat(renderedBitmaps.get(4).first).isEqualTo(10L); } @Test @@ -284,7 +295,7 @@ public class ImageRendererTest { throws Exception { FakeSampleStream fakeSampleStream1 = createSampleStream( - JPEG_FORMAT_WITH_SIX_TILES, + JPEG_FORMAT_WITH_FOUR_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), @@ -293,7 +304,7 @@ public class ImageRendererTest { fakeSampleStream1.writeData(/* startPositionUs= */ 0); FakeSampleStream fakeSampleStream2 = createSampleStream( - JPEG_FORMAT_WITH_SIX_TILES, + JPEG_FORMAT_WITH_FOUR_TILES, ImmutableList.of( oneByteSample(/* timeUs= */ 10L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM)); @@ -399,6 +410,325 @@ public class ImageRendererTest { @Test public void render_tiledImage_cropsAndRendersToImageOutput() throws Exception { + FakeSampleStream fakeSampleStream = + createSampleStream( + JPEG_FORMAT_WITH_FOUR_TILES, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), + emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + END_OF_STREAM_ITEM)); + fakeSampleStream.writeData(/* startPositionUs= */ 0); + renderer.enable( + RendererConfiguration.DEFAULT, + new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + fakeSampleStream, + /* positionUs= */ 0, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0, + new MediaSource.MediaPeriodId(new Object())); + renderer.setCurrentStreamFinal(); + + StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); + while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { + renderer.render( + /* positionUs= */ 0, + /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + } + StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); + long positionUs = 0; + while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { + renderer.render( + positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + positionUs += 100_000; + } + + assertThat(renderedBitmaps).hasSize(4); + assertThat(renderedBitmaps.get(0).first).isEqualTo(0L); + assertThat(renderedBitmaps.get(0).second.getHeight()).isEqualTo(1); + assertThat(renderedBitmaps.get(0).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(1).first).isEqualTo(100_000L); + assertThat(renderedBitmaps.get(1).second.getHeight()).isEqualTo(1); + assertThat(renderedBitmaps.get(1).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(2).first).isEqualTo(200_000L); + assertThat(renderedBitmaps.get(2).second.getHeight()).isEqualTo(1); + assertThat(renderedBitmaps.get(2).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(3).first).isEqualTo(300_000L); + assertThat(renderedBitmaps.get(3).second.getHeight()).isEqualTo(1); + assertThat(renderedBitmaps.get(3).second.getWidth()).isEqualTo(1); + } + + @Test + public void render_tiledImageWithNonZeroStartPosition_rendersToImageOutput() throws Exception { + FakeSampleStream fakeSampleStream = + createSampleStream( + JPEG_FORMAT_WITH_FOUR_TILES, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), + emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + END_OF_STREAM_ITEM)); + fakeSampleStream.writeData(/* startPositionUs= */ 0); + renderer.enable( + RendererConfiguration.DEFAULT, + new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + fakeSampleStream, + /* positionUs= */ 200_000, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0, + new MediaSource.MediaPeriodId(new Object())); + renderer.setCurrentStreamFinal(); + + StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); + while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { + renderer.render( + /* positionUs= */ 200_000, + /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + } + StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); + long positionUs = 200_000; + while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { + renderer.render( + positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + positionUs += 100_000; + } + + assertThat(renderedBitmaps).hasSize(2); + assertThat(renderedBitmaps.get(0).first).isEqualTo(200_000L); + assertThat(renderedBitmaps.get(1).first).isEqualTo(300_000L); + } + + @Test + public void render_tiledImageStartPositionIsAfterLastTile_rendersToImageOutput() + throws Exception { + FakeSampleStream fakeSampleStream = + createSampleStream( + JPEG_FORMAT_WITH_FOUR_TILES, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), + emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + END_OF_STREAM_ITEM)); + fakeSampleStream.writeData(/* startPositionUs= */ 0); + renderer.enable( + RendererConfiguration.DEFAULT, + new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + fakeSampleStream, + /* positionUs= */ 0, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0, + new MediaSource.MediaPeriodId(new Object())); + renderer.setCurrentStreamFinal(); + + StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); + while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { + renderer.render( + /* positionUs= */ 350_000, + /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + } + StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); + long positionUs = 350_000; + while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { + renderer.render( + positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + positionUs += 100_000; + } + + assertThat(renderedBitmaps).hasSize(1); + assertThat(renderedBitmaps.get(0).first).isEqualTo(300_000L); + } + + @Test + public void render_tiledImageStartPositionIsBeforeLastTileAndNotWithinThreshold_rendersPriorTile() + throws Exception { + FakeSampleStream fakeSampleStream = + createSampleStream( + JPEG_FORMAT_WITH_FOUR_TILES, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), + emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + END_OF_STREAM_ITEM)); + fakeSampleStream.writeData(/* startPositionUs= */ 0); + renderer.enable( + RendererConfiguration.DEFAULT, + new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + fakeSampleStream, + /* positionUs= */ 0, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0, + new MediaSource.MediaPeriodId(new Object())); + renderer.setCurrentStreamFinal(); + + StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); + while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { + renderer.render( + /* positionUs= */ 250_000L, + /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + } + StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); + long positionUs = 250_000L; + while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { + renderer.render( + positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + positionUs += 100_000L; + } + + assertThat(renderedBitmaps).hasSize(2); + assertThat(renderedBitmaps.get(0).first).isEqualTo(200_000L); + assertThat(renderedBitmaps.get(1).first).isEqualTo(300_000L); + } + + @Test + public void + render_tiledImageStartPositionBeforePresentationTimeAndWithinThreshold_rendersIncomingTile() + throws Exception { + FakeSampleStream fakeSampleStream = + createSampleStream( + JPEG_FORMAT_WITH_FOUR_TILES, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), + emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + END_OF_STREAM_ITEM)); + fakeSampleStream.writeData(/* startPositionUs= */ 0); + renderer.enable( + RendererConfiguration.DEFAULT, + new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + fakeSampleStream, + /* positionUs= */ 0, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0, + new MediaSource.MediaPeriodId(new Object())); + renderer.setCurrentStreamFinal(); + + StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); + while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { + renderer.render( + /* positionUs= */ 70_000, + /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + } + StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); + long positionUs = 70_000; + while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { + renderer.render( + positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + positionUs += 100_000; + } + + assertThat(renderedBitmaps).hasSize(3); + assertThat(renderedBitmaps.get(0).first).isEqualTo(100_000L); + assertThat(renderedBitmaps.get(1).first).isEqualTo(200_000L); + assertThat(renderedBitmaps.get(2).first).isEqualTo(300_000L); + } + + @Test + public void + render_tiledImageStartPositionAfterPresentationTimeAndWithinThreshold_rendersLastReadTile() + throws Exception { + FakeSampleStream fakeSampleStream = + createSampleStream( + JPEG_FORMAT_WITH_FOUR_TILES, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), + emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + END_OF_STREAM_ITEM)); + fakeSampleStream.writeData(/* startPositionUs= */ 0); + renderer.enable( + RendererConfiguration.DEFAULT, + new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + fakeSampleStream, + /* positionUs= */ 0, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0, + new MediaSource.MediaPeriodId(new Object())); + renderer.setCurrentStreamFinal(); + + StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); + while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { + renderer.render( + /* positionUs= */ 130_000, + /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + } + StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); + long positionUs = 130_000; + while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { + renderer.render( + positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + positionUs += 100_000; + } + + assertThat(renderedBitmaps).hasSize(3); + assertThat(renderedBitmaps.get(0).first).isEqualTo(100_000L); + assertThat(renderedBitmaps.get(1).first).isEqualTo(200_000L); + assertThat(renderedBitmaps.get(2).first).isEqualTo(300_000L); + } + + @Test + public void render_tiledImageStartPositionRightBeforeEOSAndWithinThreshold_rendersLastTileInGrid() + throws Exception { + FakeSampleStream fakeSampleStream = + createSampleStream( + JPEG_FORMAT_WITH_FOUR_TILES, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), + emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), + emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), + END_OF_STREAM_ITEM)); + fakeSampleStream.writeData(/* startPositionUs= */ 0); + renderer.enable( + RendererConfiguration.DEFAULT, + new Format[] {JPEG_FORMAT_WITH_FOUR_TILES}, + fakeSampleStream, + /* positionUs= */ 0, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0, + new MediaSource.MediaPeriodId(new Object())); + renderer.setCurrentStreamFinal(); + + StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); + while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { + renderer.render( + /* positionUs= */ 330_000, + /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + } + StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); + long positionUs = 330_000; + while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { + renderer.render( + positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); + positionUs += 100_000; + } + + assertThat(renderedBitmaps).hasSize(1); + assertThat(renderedBitmaps.get(0).first).isEqualTo(300_000L); + } + + @Test + public void render_tiledImageNonSquare_rendersAllImagesToOutput() throws Exception { + overridenBitmap = fakeDecodedBitmap3; FakeSampleStream fakeSampleStream = createSampleStream( JPEG_FORMAT_WITH_SIX_TILES, @@ -458,289 +788,6 @@ public class ImageRendererTest { assertThat(renderedBitmaps.get(5).second.getWidth()).isEqualTo(1); } - @Test - public void render_tiledImageWithNonZeroStartPosition_rendersToImageOutput() throws Exception { - FakeSampleStream fakeSampleStream = - createSampleStream( - JPEG_FORMAT_WITH_SIX_TILES, - ImmutableList.of( - oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), - emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), - END_OF_STREAM_ITEM)); - fakeSampleStream.writeData(/* startPositionUs= */ 0); - renderer.enable( - RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, - fakeSampleStream, - /* positionUs= */ 200_000, - /* joining= */ false, - /* mayRenderStartOfStream= */ true, - /* startPositionUs= */ 0, - /* offsetUs= */ 0, - new MediaSource.MediaPeriodId(new Object())); - renderer.setCurrentStreamFinal(); - - StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); - while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { - renderer.render( - /* positionUs= */ 200_000, - /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - } - StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); - long positionUs = 200_000; - while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { - renderer.render( - positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - positionUs += 100_000; - } - - assertThat(renderedBitmaps).hasSize(4); - assertThat(renderedBitmaps.get(0).first).isEqualTo(200_000L); - assertThat(renderedBitmaps.get(1).first).isEqualTo(300_000L); - assertThat(renderedBitmaps.get(2).first).isEqualTo(400_000L); - assertThat(renderedBitmaps.get(3).first).isEqualTo(500_000L); - } - - @Test - public void render_tiledImageStartPositionIsAfterLastTile_rendersToImageOutput() - throws Exception { - FakeSampleStream fakeSampleStream = - createSampleStream( - JPEG_FORMAT_WITH_SIX_TILES, - ImmutableList.of( - oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), - emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), - END_OF_STREAM_ITEM)); - fakeSampleStream.writeData(/* startPositionUs= */ 0); - renderer.enable( - RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, - fakeSampleStream, - /* positionUs= */ 0, - /* joining= */ false, - /* mayRenderStartOfStream= */ true, - /* startPositionUs= */ 0, - /* offsetUs= */ 0, - new MediaSource.MediaPeriodId(new Object())); - renderer.setCurrentStreamFinal(); - - StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); - while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { - renderer.render( - /* positionUs= */ 550_000, - /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - } - StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); - long positionUs = 550_000; - while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { - renderer.render( - positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - positionUs += 100_000; - } - - assertThat(renderedBitmaps).hasSize(1); - assertThat(renderedBitmaps.get(0).first).isEqualTo(500_000L); - } - - @Test - public void render_tiledImageStartPositionIsBeforeLastTileAndNotWithinThreshold_rendersPriorTile() - throws Exception { - FakeSampleStream fakeSampleStream = - createSampleStream( - JPEG_FORMAT_WITH_SIX_TILES, - ImmutableList.of( - oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), - emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), - END_OF_STREAM_ITEM)); - fakeSampleStream.writeData(/* startPositionUs= */ 0); - renderer.enable( - RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, - fakeSampleStream, - /* positionUs= */ 0, - /* joining= */ false, - /* mayRenderStartOfStream= */ true, - /* startPositionUs= */ 0, - /* offsetUs= */ 0, - new MediaSource.MediaPeriodId(new Object())); - renderer.setCurrentStreamFinal(); - - StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); - while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { - renderer.render( - /* positionUs= */ 450_000L, - /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - } - StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); - long positionUs = 450_000L; - while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { - renderer.render( - positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - positionUs += 100_000L; - } - - assertThat(renderedBitmaps).hasSize(2); - assertThat(renderedBitmaps.get(0).first).isEqualTo(400_000L); - assertThat(renderedBitmaps.get(1).first).isEqualTo(500_000L); - } - - @Test - public void - render_tiledImageStartPositionBeforePresentationTimeAndWithinThreshold_rendersIncomingTile() - throws Exception { - FakeSampleStream fakeSampleStream = - createSampleStream( - JPEG_FORMAT_WITH_SIX_TILES, - ImmutableList.of( - oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), - emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), - END_OF_STREAM_ITEM)); - fakeSampleStream.writeData(/* startPositionUs= */ 0); - renderer.enable( - RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, - fakeSampleStream, - /* positionUs= */ 0, - /* joining= */ false, - /* mayRenderStartOfStream= */ true, - /* startPositionUs= */ 0, - /* offsetUs= */ 0, - new MediaSource.MediaPeriodId(new Object())); - renderer.setCurrentStreamFinal(); - - StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); - while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { - renderer.render( - /* positionUs= */ 70_000, - /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - } - StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); - long positionUs = 70_000; - while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { - renderer.render( - positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - positionUs += 100_000; - } - - assertThat(renderedBitmaps).hasSize(5); - assertThat(renderedBitmaps.get(0).first).isEqualTo(100_000L); - assertThat(renderedBitmaps.get(1).first).isEqualTo(200_000L); - assertThat(renderedBitmaps.get(2).first).isEqualTo(300_000L); - assertThat(renderedBitmaps.get(3).first).isEqualTo(400_000L); - assertThat(renderedBitmaps.get(4).first).isEqualTo(500_000L); - } - - @Test - public void - render_tiledImageStartPositionAfterPresentationTimeAndWithinThreshold_rendersLastReadTile() - throws Exception { - FakeSampleStream fakeSampleStream = - createSampleStream( - JPEG_FORMAT_WITH_SIX_TILES, - ImmutableList.of( - oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), - emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), - END_OF_STREAM_ITEM)); - fakeSampleStream.writeData(/* startPositionUs= */ 0); - renderer.enable( - RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, - fakeSampleStream, - /* positionUs= */ 0, - /* joining= */ false, - /* mayRenderStartOfStream= */ true, - /* startPositionUs= */ 0, - /* offsetUs= */ 0, - new MediaSource.MediaPeriodId(new Object())); - renderer.setCurrentStreamFinal(); - - StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); - while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { - renderer.render( - /* positionUs= */ 130_000, - /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - } - StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); - long positionUs = 130_000; - while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { - renderer.render( - positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - positionUs += 100_000; - } - - assertThat(renderedBitmaps).hasSize(5); - assertThat(renderedBitmaps.get(0).first).isEqualTo(100_000L); - assertThat(renderedBitmaps.get(1).first).isEqualTo(200_000L); - assertThat(renderedBitmaps.get(2).first).isEqualTo(300_000L); - assertThat(renderedBitmaps.get(3).first).isEqualTo(400_000L); - assertThat(renderedBitmaps.get(4).first).isEqualTo(500_000L); - } - - @Test - public void render_tiledImageStartPositionRightBeforeEOSAndWithinThreshold_rendersLastTileInGrid() - throws Exception { - FakeSampleStream fakeSampleStream = - createSampleStream( - JPEG_FORMAT_WITH_SIX_TILES, - ImmutableList.of( - oneByteSample(/* timeUs= */ 0L, /* flags= */ C.BUFFER_FLAG_KEY_FRAME), - emptySample(/* timeUs= */ 100_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 200_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 300_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 400_000L, /* flags= */ 0), - emptySample(/* timeUs= */ 500_000L, /* flags= */ 0), - END_OF_STREAM_ITEM)); - fakeSampleStream.writeData(/* startPositionUs= */ 0); - renderer.enable( - RendererConfiguration.DEFAULT, - new Format[] {JPEG_FORMAT_WITH_SIX_TILES}, - fakeSampleStream, - /* positionUs= */ 0, - /* joining= */ false, - /* mayRenderStartOfStream= */ true, - /* startPositionUs= */ 0, - /* offsetUs= */ 0, - new MediaSource.MediaPeriodId(new Object())); - renderer.setCurrentStreamFinal(); - - StopWatch isReadyStopWatch = new StopWatch(IS_READY_TIMEOUT_MESSAGE); - while (!renderer.isReady() && isReadyStopWatch.ensureNotExpired()) { - renderer.render( - /* positionUs= */ 530_000, - /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - } - StopWatch isEndedStopWatch = new StopWatch(IS_ENDED_TIMEOUT_MESSAGE); - long positionUs = 330_000; - while (!renderer.isEnded() && isEndedStopWatch.ensureNotExpired()) { - renderer.render( - positionUs, /* elapsedRealtimeUs= */ SystemClock.DEFAULT.elapsedRealtime() * 1000); - positionUs += 100_000; - } - - assertThat(renderedBitmaps).hasSize(1); - assertThat(renderedBitmaps.get(0).first).isEqualTo(500_000L); - } - private static FakeSampleStream.FakeSampleStreamItem emptySample( long timeUs, @C.BufferFlags int flags) { return sample(timeUs, flags, new byte[] {}); From cf41ba3a3d2330cb291420c639df427e10ecb434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Schia?= Date: Tue, 30 Apr 2024 16:42:11 +0200 Subject: [PATCH 3/6] Create new ImageRenderer in render_tiledImageNonSquare_rendersAllImagesToOutput to allow it to use a separate bitmap from the other tests --- .../exoplayer/image/ImageRendererTest.java | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java index badac24f98..7d2e587e0f 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java @@ -84,25 +84,16 @@ public class ImageRendererTest { Bitmap.createBitmap(/* width= */ 2, /* height= */ 2, Bitmap.Config.ARGB_8888); private final Bitmap fakeDecodedBitmap2 = Bitmap.createBitmap(/* width= */ 4, /* height= */ 4, Bitmap.Config.ARGB_8888); - private final Bitmap fakeDecodedBitmap3 = - Bitmap.createBitmap(/* width= */ 2, /* height= */ 3, Bitmap.Config.ARGB_8888); private ImageRenderer renderer; private int decodeCallCount; - private Bitmap overridenBitmap = null; @Before public void setUp() throws Exception { decodeCallCount = 0; ImageDecoder.Factory fakeDecoderFactory = new BitmapFactoryImageDecoder.Factory( - (data, length) -> { - if (overridenBitmap != null) { - return overridenBitmap; - } else { - return ++decodeCallCount == 1 ? fakeDecodedBitmap1 : fakeDecodedBitmap2; - } - }); + (data, length) -> ++decodeCallCount == 1 ? fakeDecodedBitmap1 : fakeDecodedBitmap2); ImageOutput queuingImageOutput = new ImageOutput() { @Override @@ -728,7 +719,26 @@ public class ImageRendererTest { @Test public void render_tiledImageNonSquare_rendersAllImagesToOutput() throws Exception { - overridenBitmap = fakeDecodedBitmap3; + ImageDecoder.Factory fakeDecoderFactory = + new BitmapFactoryImageDecoder.Factory( + (data, length) -> + Bitmap.createBitmap(/* width= */ 2, /* height= */ 3, Bitmap.Config.ARGB_8888)); + ImageOutput queuingImageOutput = + new ImageOutput() { + @Override + public void onImageAvailable(long presentationTimeUs, Bitmap bitmap) { + renderedBitmaps.add(Pair.create(presentationTimeUs, bitmap)); + } + + @Override + public void onDisabled() { + // Do nothing. + } + }; + + renderer = new ImageRenderer(fakeDecoderFactory, queuingImageOutput); + renderer.init(/* index= */ 0, PlayerId.UNSET, Clock.DEFAULT); + FakeSampleStream fakeSampleStream = createSampleStream( JPEG_FORMAT_WITH_SIX_TILES, From 6d62d3437cc822620e864ba95f25b3170b65d0d7 Mon Sep 17 00:00:00 2001 From: microkatz Date: Wed, 1 May 2024 11:16:13 +0000 Subject: [PATCH 4/6] Format with google-java-format --- .../java/androidx/media3/exoplayer/image/ImageRendererTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java index 7d2e587e0f..0725e03bf2 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java @@ -735,10 +735,8 @@ public class ImageRendererTest { // Do nothing. } }; - renderer = new ImageRenderer(fakeDecoderFactory, queuingImageOutput); renderer.init(/* index= */ 0, PlayerId.UNSET, Clock.DEFAULT); - FakeSampleStream fakeSampleStream = createSampleStream( JPEG_FORMAT_WITH_SIX_TILES, From 690bb1ace3fd9dcf45c9afa123cbaedcf7f07d1f Mon Sep 17 00:00:00 2001 From: microkatz Date: Wed, 1 May 2024 11:21:40 +0000 Subject: [PATCH 5/6] Add release note for DASH non-square thumbnail grid fix --- RELEASENOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index a807bafd0c..0a3ee15d7b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -45,6 +45,8 @@ * Propagate ID3 `TCON` frame to `MediaMetadata.genre` ([#1305](https://github.com/androidx/media/issues/1305)). * Image: + * Add support for non-square DASH thumbnail grids + ([#1255](https://github.com/androidx/media/pull/1300)). * DRM: * Allow setting a `LoadErrorHandlingPolicy` on `DefaultDrmSessionManagerProvider` From acc5a3bcd32d80d151fcb4d8ef24d0c8f2024029 Mon Sep 17 00:00:00 2001 From: microkatz Date: Wed, 1 May 2024 14:15:40 +0000 Subject: [PATCH 6/6] Updated non-Square thumbnail unit test to contain non-Square thumbnails --- .../exoplayer/image/ImageRendererTest.java | 51 +++++++++++++------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java index 0725e03bf2..b514d36d22 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/image/ImageRendererTest.java @@ -21,6 +21,7 @@ import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.s import static com.google.common.truth.Truth.assertThat; import android.graphics.Bitmap; +import android.graphics.Color; import android.util.Pair; import androidx.media3.common.C; import androidx.media3.common.Format; @@ -75,8 +76,8 @@ public class ImageRendererTest { private static final Format JPEG_FORMAT_WITH_SIX_TILES = new Format.Builder() .setSampleMimeType(MimeTypes.IMAGE_JPEG) - .setTileCountVertical(3) - .setTileCountHorizontal(2) + .setTileCountVertical(2) + .setTileCountHorizontal(3) .build(); private final List> renderedBitmaps = new ArrayList<>(); @@ -721,8 +722,24 @@ public class ImageRendererTest { public void render_tiledImageNonSquare_rendersAllImagesToOutput() throws Exception { ImageDecoder.Factory fakeDecoderFactory = new BitmapFactoryImageDecoder.Factory( - (data, length) -> - Bitmap.createBitmap(/* width= */ 2, /* height= */ 3, Bitmap.Config.ARGB_8888)); + (data, length) -> { + /* + * Thumbnail grid image is as depicted below. + * 0 1 2 3 4 5 6 7 8 + * ----------------- + * 0 | T0 | T1 | T2 | + * 1 | | | | + * ----------------- + * 2 | T3 | T4 | T5 | + * 3 | | | | + * ----------------- + */ + Bitmap bm = + Bitmap.createBitmap(/* width= */ 9, /* height= */ 4, Bitmap.Config.ARGB_8888); + bm.setPixel(1, 2, Color.rgb(100, 0, 0)); + bm.setPixel(4, 3, Color.rgb(0, 100, 0)); + return bm; + }); ImageOutput queuingImageOutput = new ImageOutput() { @Override @@ -777,23 +794,25 @@ public class ImageRendererTest { assertThat(renderedBitmaps).hasSize(6); assertThat(renderedBitmaps.get(0).first).isEqualTo(0L); - assertThat(renderedBitmaps.get(0).second.getHeight()).isEqualTo(1); - assertThat(renderedBitmaps.get(0).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(0).second.getHeight()).isEqualTo(2); + assertThat(renderedBitmaps.get(0).second.getWidth()).isEqualTo(3); assertThat(renderedBitmaps.get(1).first).isEqualTo(100_000L); - assertThat(renderedBitmaps.get(1).second.getHeight()).isEqualTo(1); - assertThat(renderedBitmaps.get(1).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(1).second.getHeight()).isEqualTo(2); + assertThat(renderedBitmaps.get(1).second.getWidth()).isEqualTo(3); assertThat(renderedBitmaps.get(2).first).isEqualTo(200_000L); - assertThat(renderedBitmaps.get(2).second.getHeight()).isEqualTo(1); - assertThat(renderedBitmaps.get(2).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(2).second.getHeight()).isEqualTo(2); + assertThat(renderedBitmaps.get(2).second.getWidth()).isEqualTo(3); assertThat(renderedBitmaps.get(3).first).isEqualTo(300_000L); - assertThat(renderedBitmaps.get(3).second.getHeight()).isEqualTo(1); - assertThat(renderedBitmaps.get(3).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(3).second.getHeight()).isEqualTo(2); + assertThat(renderedBitmaps.get(3).second.getWidth()).isEqualTo(3); + assertThat(renderedBitmaps.get(3).second.getPixel(1, 0)).isEqualTo(Color.rgb(100, 0, 0)); assertThat(renderedBitmaps.get(4).first).isEqualTo(400_000L); - assertThat(renderedBitmaps.get(4).second.getHeight()).isEqualTo(1); - assertThat(renderedBitmaps.get(4).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(4).second.getHeight()).isEqualTo(2); + assertThat(renderedBitmaps.get(4).second.getWidth()).isEqualTo(3); + assertThat(renderedBitmaps.get(4).second.getPixel(1,1)).isEqualTo(Color.rgb(0, 100, 0)); assertThat(renderedBitmaps.get(5).first).isEqualTo(500_000L); - assertThat(renderedBitmaps.get(5).second.getHeight()).isEqualTo(1); - assertThat(renderedBitmaps.get(5).second.getWidth()).isEqualTo(1); + assertThat(renderedBitmaps.get(5).second.getHeight()).isEqualTo(2); + assertThat(renderedBitmaps.get(5).second.getWidth()).isEqualTo(3); } private static FakeSampleStream.FakeSampleStreamItem emptySample(