From 638b2a3c86f7362041b09faed7c3c09609e29118 Mon Sep 17 00:00:00 2001 From: michaelkatz Date: Wed, 6 Mar 2024 08:47:42 -0800 Subject: [PATCH] Start early-enabled renderers only after advancing the playing period Renderers may be enabled for subsequent media items as soon as the current media item's renderer's isEnded() returns true. When a renderer is being enabled and the player is 'playing', that renderer is also started. When playing a mixed playlist of images and content with audio & video, the player may skip some image items because the early-starting of the audio renderer causes a clock update. A solution is to only start the "early-enabled" renderers at the point of media transition and add a condition on DefaultMediaClock to use the standalone clock when reading-ahead and the renderer clock source is not in a started state. Issue: androidx/media#1017 PiperOrigin-RevId: 613231227 --- RELEASENOTES.md | 3 + .../media3/exoplayer/DefaultMediaClock.java | 6 +- .../exoplayer/ExoPlayerImplInternal.java | 26 +- .../e2etest/PlaylistPlaybackTest.java | 41 ++ .../playlists/image_av_playlist.dump | 692 ++++++++++++++++++ 5 files changed, 761 insertions(+), 7 deletions(-) create mode 100644 libraries/test_data/src/test/assets/playbackdumps/playlists/image_av_playlist.dump diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 5e120311c8b..affde98c5d1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -16,6 +16,9 @@ that indicates the index of an item on the UI. * Add `PlayerId` to most methods of `LoadControl` to enable `LoadControl` implementations to support multiple players. + * Start early-enabled renderers only after advancing the playing period + when transitioning between media items + ([#1017](https://github.com/androidx/media/issues/1017)). * Transformer: * Add `audioConversionProcess` and `videoConversionProcess` to `ExportResult` indicating how the respective track in the output file diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultMediaClock.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultMediaClock.java index 7677a0c5e69..6bfd495fd49 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultMediaClock.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultMediaClock.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer; +import static androidx.media3.exoplayer.Renderer.STATE_STARTED; + import androidx.annotation.Nullable; import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackParameters; @@ -192,12 +194,14 @@ private void syncClocks(boolean isReadingAhead) { } private boolean shouldUseStandaloneClock(boolean isReadingAhead) { - // Use the standalone clock if the clock providing renderer is not set or has ended. Also use + // Use the standalone clock if the clock providing renderer is not set or has ended. Use the + // standalone clock if reading ahead and the renderer is not in a started state. Also use // the standalone clock if the renderer is not ready and we have finished reading the stream or // are reading ahead to avoid getting stuck if tracks in the current period have uneven // durations. See: https://github.com/google/ExoPlayer/issues/1874. return rendererClockSource == null || rendererClockSource.isEnded() + || (isReadingAhead && rendererClockSource.getState() != STATE_STARTED) || (!rendererClockSource.isReady() && (isReadingAhead || rendererClockSource.hasReadStreamToEnd())); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index 07205cea5b9..6df6b37fca6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -18,6 +18,9 @@ import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.castNonNull; import static androidx.media3.common.util.Util.msToUs; +import static androidx.media3.exoplayer.Renderer.STATE_DISABLED; +import static androidx.media3.exoplayer.Renderer.STATE_ENABLED; +import static androidx.media3.exoplayer.Renderer.STATE_STARTED; import static androidx.media3.exoplayer.audio.AudioSink.OFFLOAD_MODE_DISABLED; import static java.lang.Math.max; import static java.lang.Math.min; @@ -76,6 +79,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -1779,7 +1783,7 @@ private void maybeTriggerPendingMessages(long oldPeriodPositionUs, long newPerio } private void ensureStopped(Renderer renderer) { - if (renderer.getState() == Renderer.STATE_STARTED) { + if (renderer.getState() == STATE_STARTED) { renderer.stop(); } } @@ -2322,6 +2326,16 @@ private void maybeUpdatePlayingPeriod() throws ExoPlaybackException { Player.DISCONTINUITY_REASON_AUTO_TRANSITION); resetPendingPauseAtEndOfPeriod(); updatePlaybackPositions(); + if (playbackInfo.playbackState == Player.STATE_READY) { + for (int i = 0; i < renderers.length; i++) { + if (renderers[i].getState() == STATE_ENABLED + && queue.getPlayingPeriod() != null + && Objects.equals( + renderers[i].getStream(), queue.getPlayingPeriod().sampleStreams[i])) { + renderers[i].start(); + } + } + } allowRenderersToRenderStartOfStreams(); advancedPlayingPeriod = true; } @@ -2678,7 +2692,7 @@ private void enableRenderer(int rendererIndex, boolean wasRendererEnabled, long return; } MediaPeriodHolder periodHolder = queue.getReadingPeriod(); - boolean mayRenderStartOfStream = periodHolder == queue.getPlayingPeriod(); + boolean arePlayingAndReadingTheSamePeriod = periodHolder == queue.getPlayingPeriod(); TrackSelectorResult trackSelectorResult = periodHolder.getTrackSelectorResult(); RendererConfiguration rendererConfiguration = trackSelectorResult.rendererConfigurations[rendererIndex]; @@ -2697,7 +2711,7 @@ private void enableRenderer(int rendererIndex, boolean wasRendererEnabled, long periodHolder.sampleStreams[rendererIndex], rendererPositionUs, joining, - mayRenderStartOfStream, + /* mayRenderStartOfStream= */ arePlayingAndReadingTheSamePeriod, startPositionUs, periodHolder.getRendererOffset(), periodHolder.info.id); @@ -2716,8 +2730,8 @@ public void onWakeup() { }); mediaClock.onRendererEnabled(renderer); - // Start the renderer if playing. - if (playing) { + // Start the renderer if playing and the Playing and Reading periods are the same. + if (playing && arePlayingAndReadingTheSamePeriod) { renderer.start(); } } @@ -3238,7 +3252,7 @@ private static Format[] getFormats(ExoTrackSelection newSelection) { } private static boolean isRendererEnabled(Renderer renderer) { - return renderer.getState() != Renderer.STATE_DISABLED; + return renderer.getState() != STATE_DISABLED; } private static final class SeekPosition { diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/PlaylistPlaybackTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/PlaylistPlaybackTest.java index 5fcebe2c6d2..7c5d0dfb11a 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/PlaylistPlaybackTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/PlaylistPlaybackTest.java @@ -15,6 +15,9 @@ */ package androidx.media3.exoplayer.e2etest; +import static com.google.common.truth.Truth.assertThat; +import static org.robolectric.annotation.GraphicsMode.Mode.NATIVE; + import android.content.Context; import android.graphics.SurfaceTexture; import android.net.Uri; @@ -23,6 +26,7 @@ import androidx.media3.common.MediaItem; import androidx.media3.common.MimeTypes; import androidx.media3.common.Player; +import androidx.media3.common.util.Clock; import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; import androidx.media3.exoplayer.source.MediaSource; @@ -38,9 +42,11 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.annotation.GraphicsMode; /** End-to-end tests for playlists. */ @RunWith(AndroidJUnit4.class) +@GraphicsMode(value = NATIVE) public final class PlaylistPlaybackTest { @Rule @@ -141,4 +147,39 @@ public void test_subtitle() throws Exception { DumpFileAsserts.assertOutput( applicationContext, playbackOutput, "playbackdumps/playlists/playlist_with_subtitles.dump"); } + + @Test + public void testPlaylist_withImageAndAudioVideoItems_rendersExpectedContent() throws Exception { + Context applicationContext = ApplicationProvider.getApplicationContext(); + CapturingRenderersFactory renderersFactory = new CapturingRenderersFactory(applicationContext); + Clock clock = new FakeClock(/* isAutoAdvancing= */ true); + ExoPlayer player = + new ExoPlayer.Builder(applicationContext, renderersFactory).setClock(clock).build(); + PlaybackOutput playbackOutput = PlaybackOutput.register(player, renderersFactory); + long durationMs = 5 * C.MILLIS_PER_SECOND; + player.setMediaItems( + ImmutableList.of( + new MediaItem.Builder() + .setUri("asset:///media/png/media3test.png") + .setImageDurationMs(durationMs) + .build(), + new MediaItem.Builder() + .setUri("asset:///media/png/media3test.png") + .setImageDurationMs(durationMs) + .build(), + MediaItem.fromUri("asset:///media/mp4/sample.mp4"))); + player.prepare(); + + TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY); + long playerStartedMs = clock.elapsedRealtime(); + player.play(); + TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED); + long playbackDurationMs = clock.elapsedRealtime() - playerStartedMs; + player.release(); + + // Playback duration should be greater than the sum of the image item durations. + assertThat(playbackDurationMs).isGreaterThan(durationMs * 2); + DumpFileAsserts.assertOutput( + applicationContext, playbackOutput, "playbackdumps/playlists/image_av_playlist.dump"); + } } diff --git a/libraries/test_data/src/test/assets/playbackdumps/playlists/image_av_playlist.dump b/libraries/test_data/src/test/assets/playbackdumps/playlists/image_av_playlist.dump new file mode 100644 index 00000000000..00d59671985 --- /dev/null +++ b/libraries/test_data/src/test/assets/playbackdumps/playlists/image_av_playlist.dump @@ -0,0 +1,692 @@ +MediaCodecAdapter (exotest.audio.aac): + inputBuffers: + count = 46 + input buffer #0: + timeUs = 1000010044000 + contents = length 23, hash 47DE9131 + input buffer #1: + timeUs = 1000010067219 + contents = length 6, hash 31EC5206 + input buffer #2: + timeUs = 1000010090439 + contents = length 148, hash 894A176B + input buffer #3: + timeUs = 1000010113659 + contents = length 189, hash CEF235A1 + input buffer #4: + timeUs = 1000010136879 + contents = length 205, hash BBF5F7B0 + input buffer #5: + timeUs = 1000010160099 + contents = length 210, hash F278B193 + input buffer #6: + timeUs = 1000010183319 + contents = length 210, hash 82DA1589 + input buffer #7: + timeUs = 1000010206539 + contents = length 207, hash 5BE231DF + input buffer #8: + timeUs = 1000010229759 + contents = length 225, hash 18819EE1 + input buffer #9: + timeUs = 1000010252979 + contents = length 215, hash CA7FA67B + input buffer #10: + timeUs = 1000010276199 + contents = length 211, hash 581A1C18 + input buffer #11: + timeUs = 1000010299419 + contents = length 216, hash ADB88187 + input buffer #12: + timeUs = 1000010322639 + contents = length 229, hash 2E8BA4DC + input buffer #13: + timeUs = 1000010345859 + contents = length 232, hash 22F0C510 + input buffer #14: + timeUs = 1000010369079 + contents = length 235, hash 867AD0DC + input buffer #15: + timeUs = 1000010392299 + contents = length 231, hash 84E823A8 + input buffer #16: + timeUs = 1000010415519 + contents = length 226, hash 1BEF3A95 + input buffer #17: + timeUs = 1000010438739 + contents = length 216, hash EAA345AE + input buffer #18: + timeUs = 1000010461959 + contents = length 229, hash 6957411F + input buffer #19: + timeUs = 1000010485179 + contents = length 219, hash 41275022 + input buffer #20: + timeUs = 1000010508399 + contents = length 241, hash 6495DF96 + input buffer #21: + timeUs = 1000010531619 + contents = length 228, hash 63D95906 + input buffer #22: + timeUs = 1000010554839 + contents = length 238, hash 34F676F9 + input buffer #23: + timeUs = 1000010578058 + contents = length 234, hash E5CBC045 + input buffer #24: + timeUs = 1000010601278 + contents = length 231, hash 5FC43661 + input buffer #25: + timeUs = 1000010624498 + contents = length 217, hash 682708ED + input buffer #26: + timeUs = 1000010647718 + contents = length 239, hash D43780FC + input buffer #27: + timeUs = 1000010670938 + contents = length 243, hash C5E17980 + input buffer #28: + timeUs = 1000010694158 + contents = length 231, hash AC5837BA + input buffer #29: + timeUs = 1000010717378 + contents = length 230, hash 169EE895 + input buffer #30: + timeUs = 1000010740598 + contents = length 238, hash C48FF3F1 + input buffer #31: + timeUs = 1000010763818 + contents = length 225, hash 531E4599 + input buffer #32: + timeUs = 1000010787038 + contents = length 232, hash CB3C6B8D + input buffer #33: + timeUs = 1000010810258 + contents = length 243, hash F8C94C7 + input buffer #34: + timeUs = 1000010833478 + contents = length 232, hash A646A7D0 + input buffer #35: + timeUs = 1000010856698 + contents = length 237, hash E8B787A5 + input buffer #36: + timeUs = 1000010879918 + contents = length 228, hash 3FA7A29F + input buffer #37: + timeUs = 1000010903138 + contents = length 235, hash B9B33B0A + input buffer #38: + timeUs = 1000010926358 + contents = length 264, hash 71A4869E + input buffer #39: + timeUs = 1000010949578 + contents = length 257, hash D049B54C + input buffer #40: + timeUs = 1000010972798 + contents = length 227, hash 66757231 + input buffer #41: + timeUs = 1000010996018 + contents = length 227, hash BD374F1B + input buffer #42: + timeUs = 1000011019238 + contents = length 235, hash 999477F6 + input buffer #43: + timeUs = 1000011042458 + contents = length 229, hash FFF98DF0 + input buffer #44: + timeUs = 1000011065678 + contents = length 6, hash 31B22286 + input buffer #45: + timeUs = 0 + flags = 4 + contents = length 0, hash 1 + outputBuffers: + count = 45 + output buffer #0: + timeUs = 1000010044000 + size = 0 + rendered = false + output buffer #1: + timeUs = 1000010067219 + size = 0 + rendered = false + output buffer #2: + timeUs = 1000010090439 + size = 0 + rendered = false + output buffer #3: + timeUs = 1000010113659 + size = 0 + rendered = false + output buffer #4: + timeUs = 1000010136879 + size = 0 + rendered = false + output buffer #5: + timeUs = 1000010160099 + size = 0 + rendered = false + output buffer #6: + timeUs = 1000010183319 + size = 0 + rendered = false + output buffer #7: + timeUs = 1000010206539 + size = 0 + rendered = false + output buffer #8: + timeUs = 1000010229759 + size = 0 + rendered = false + output buffer #9: + timeUs = 1000010252979 + size = 0 + rendered = false + output buffer #10: + timeUs = 1000010276199 + size = 0 + rendered = false + output buffer #11: + timeUs = 1000010299419 + size = 0 + rendered = false + output buffer #12: + timeUs = 1000010322639 + size = 0 + rendered = false + output buffer #13: + timeUs = 1000010345859 + size = 0 + rendered = false + output buffer #14: + timeUs = 1000010369079 + size = 0 + rendered = false + output buffer #15: + timeUs = 1000010392299 + size = 0 + rendered = false + output buffer #16: + timeUs = 1000010415519 + size = 0 + rendered = false + output buffer #17: + timeUs = 1000010438739 + size = 0 + rendered = false + output buffer #18: + timeUs = 1000010461959 + size = 0 + rendered = false + output buffer #19: + timeUs = 1000010485179 + size = 0 + rendered = false + output buffer #20: + timeUs = 1000010508399 + size = 0 + rendered = false + output buffer #21: + timeUs = 1000010531619 + size = 0 + rendered = false + output buffer #22: + timeUs = 1000010554839 + size = 0 + rendered = false + output buffer #23: + timeUs = 1000010578058 + size = 0 + rendered = false + output buffer #24: + timeUs = 1000010601278 + size = 0 + rendered = false + output buffer #25: + timeUs = 1000010624498 + size = 0 + rendered = false + output buffer #26: + timeUs = 1000010647718 + size = 0 + rendered = false + output buffer #27: + timeUs = 1000010670938 + size = 0 + rendered = false + output buffer #28: + timeUs = 1000010694158 + size = 0 + rendered = false + output buffer #29: + timeUs = 1000010717378 + size = 0 + rendered = false + output buffer #30: + timeUs = 1000010740598 + size = 0 + rendered = false + output buffer #31: + timeUs = 1000010763818 + size = 0 + rendered = false + output buffer #32: + timeUs = 1000010787038 + size = 0 + rendered = false + output buffer #33: + timeUs = 1000010810258 + size = 0 + rendered = false + output buffer #34: + timeUs = 1000010833478 + size = 0 + rendered = false + output buffer #35: + timeUs = 1000010856698 + size = 0 + rendered = false + output buffer #36: + timeUs = 1000010879918 + size = 0 + rendered = false + output buffer #37: + timeUs = 1000010903138 + size = 0 + rendered = false + output buffer #38: + timeUs = 1000010926358 + size = 0 + rendered = false + output buffer #39: + timeUs = 1000010949578 + size = 0 + rendered = false + output buffer #40: + timeUs = 1000010972798 + size = 0 + rendered = false + output buffer #41: + timeUs = 1000010996018 + size = 0 + rendered = false + output buffer #42: + timeUs = 1000011019238 + size = 0 + rendered = false + output buffer #43: + timeUs = 1000011042458 + size = 0 + rendered = false + output buffer #44: + timeUs = 1000011065678 + size = 0 + rendered = false +MediaCodecAdapter (exotest.video.avc): + inputBuffers: + count = 31 + input buffer #0: + timeUs = 1000010000000 + contents = length 36692, hash D216076E + input buffer #1: + timeUs = 1000010066733 + contents = length 5312, hash D45D3CA0 + input buffer #2: + timeUs = 1000010033366 + contents = length 599, hash 1BE7812D + input buffer #3: + timeUs = 1000010200200 + contents = length 7735, hash 4490F110 + input buffer #4: + timeUs = 1000010133466 + contents = length 987, hash 560B5036 + input buffer #5: + timeUs = 1000010100100 + contents = length 673, hash ED7CD8C7 + input buffer #6: + timeUs = 1000010166833 + contents = length 523, hash 3020DF50 + input buffer #7: + timeUs = 1000010333666 + contents = length 6061, hash 736C72B2 + input buffer #8: + timeUs = 1000010266933 + contents = length 992, hash FE132F23 + input buffer #9: + timeUs = 1000010233566 + contents = length 623, hash 5B2C1816 + input buffer #10: + timeUs = 1000010300300 + contents = length 421, hash 742E69C1 + input buffer #11: + timeUs = 1000010433766 + contents = length 4899, hash F72F86A1 + input buffer #12: + timeUs = 1000010400400 + contents = length 568, hash 519A8E50 + input buffer #13: + timeUs = 1000010367033 + contents = length 620, hash 3990AA39 + input buffer #14: + timeUs = 1000010567233 + contents = length 5450, hash F06EC4AA + input buffer #15: + timeUs = 1000010500500 + contents = length 1051, hash 92DFA63A + input buffer #16: + timeUs = 1000010467133 + contents = length 874, hash 69587FB4 + input buffer #17: + timeUs = 1000010533866 + contents = length 781, hash 36BE495B + input buffer #18: + timeUs = 1000010700700 + contents = length 4725, hash AC0C8CD3 + input buffer #19: + timeUs = 1000010633966 + contents = length 1022, hash 5D8BFF34 + input buffer #20: + timeUs = 1000010600600 + contents = length 790, hash 99413A99 + input buffer #21: + timeUs = 1000010667333 + contents = length 610, hash 5E129290 + input buffer #22: + timeUs = 1000010834166 + contents = length 2751, hash 769974CB + input buffer #23: + timeUs = 1000010767433 + contents = length 745, hash B78A477A + input buffer #24: + timeUs = 1000010734066 + contents = length 621, hash CF741E7A + input buffer #25: + timeUs = 1000010800800 + contents = length 505, hash 1DB4894E + input buffer #26: + timeUs = 1000010967633 + contents = length 1268, hash C15348DC + input buffer #27: + timeUs = 1000010900900 + contents = length 880, hash C2DE85D0 + input buffer #28: + timeUs = 1000010867533 + contents = length 530, hash C98BC6A8 + input buffer #29: + timeUs = 1000010934266 + contents = length 568, hash 4FE5C8EA + input buffer #30: + timeUs = 0 + flags = 4 + contents = length 0, hash 1 + outputBuffers: + count = 30 + output buffer #0: + timeUs = 1000010000000 + size = 36692 + rendered = false + output buffer #1: + timeUs = 1000010066733 + size = 5312 + rendered = false + output buffer #2: + timeUs = 1000010033366 + size = 599 + rendered = false + output buffer #3: + timeUs = 1000010200200 + size = 7735 + rendered = false + output buffer #4: + timeUs = 1000010133466 + size = 987 + rendered = false + output buffer #5: + timeUs = 1000010100100 + size = 673 + rendered = false + output buffer #6: + timeUs = 1000010166833 + size = 523 + rendered = false + output buffer #7: + timeUs = 1000010333666 + size = 6061 + rendered = false + output buffer #8: + timeUs = 1000010266933 + size = 992 + rendered = false + output buffer #9: + timeUs = 1000010233566 + size = 623 + rendered = false + output buffer #10: + timeUs = 1000010300300 + size = 421 + rendered = false + output buffer #11: + timeUs = 1000010433766 + size = 4899 + rendered = false + output buffer #12: + timeUs = 1000010400400 + size = 568 + rendered = false + output buffer #13: + timeUs = 1000010367033 + size = 620 + rendered = false + output buffer #14: + timeUs = 1000010567233 + size = 5450 + rendered = false + output buffer #15: + timeUs = 1000010500500 + size = 1051 + rendered = false + output buffer #16: + timeUs = 1000010467133 + size = 874 + rendered = false + output buffer #17: + timeUs = 1000010533866 + size = 781 + rendered = false + output buffer #18: + timeUs = 1000010700700 + size = 4725 + rendered = false + output buffer #19: + timeUs = 1000010633966 + size = 1022 + rendered = false + output buffer #20: + timeUs = 1000010600600 + size = 790 + rendered = false + output buffer #21: + timeUs = 1000010667333 + size = 610 + rendered = false + output buffer #22: + timeUs = 1000010834166 + size = 2751 + rendered = false + output buffer #23: + timeUs = 1000010767433 + size = 745 + rendered = false + output buffer #24: + timeUs = 1000010734066 + size = 621 + rendered = false + output buffer #25: + timeUs = 1000010800800 + size = 505 + rendered = false + output buffer #26: + timeUs = 1000010967633 + size = 1268 + rendered = false + output buffer #27: + timeUs = 1000010900900 + size = 880 + rendered = false + output buffer #28: + timeUs = 1000010867533 + size = 530 + rendered = false + output buffer #29: + timeUs = 1000010934266 + size = 568 + rendered = false +AudioSink: + buffer count = 45 + config: + pcmEncoding = 2 + channelCount = 1 + sampleRate = 44100 + buffer #0: + time = 1000010044000 + data = 1 + buffer #1: + time = 1000010067219 + data = 1 + buffer #2: + time = 1000010090439 + data = 1 + buffer #3: + time = 1000010113659 + data = 1 + buffer #4: + time = 1000010136879 + data = 1 + buffer #5: + time = 1000010160099 + data = 1 + buffer #6: + time = 1000010183319 + data = 1 + buffer #7: + time = 1000010206539 + data = 1 + buffer #8: + time = 1000010229759 + data = 1 + buffer #9: + time = 1000010252979 + data = 1 + buffer #10: + time = 1000010276199 + data = 1 + buffer #11: + time = 1000010299419 + data = 1 + buffer #12: + time = 1000010322639 + data = 1 + buffer #13: + time = 1000010345859 + data = 1 + buffer #14: + time = 1000010369079 + data = 1 + buffer #15: + time = 1000010392299 + data = 1 + buffer #16: + time = 1000010415519 + data = 1 + buffer #17: + time = 1000010438739 + data = 1 + buffer #18: + time = 1000010461959 + data = 1 + buffer #19: + time = 1000010485179 + data = 1 + buffer #20: + time = 1000010508399 + data = 1 + buffer #21: + time = 1000010531619 + data = 1 + buffer #22: + time = 1000010554839 + data = 1 + buffer #23: + time = 1000010578058 + data = 1 + buffer #24: + time = 1000010601278 + data = 1 + buffer #25: + time = 1000010624498 + data = 1 + buffer #26: + time = 1000010647718 + data = 1 + buffer #27: + time = 1000010670938 + data = 1 + buffer #28: + time = 1000010694158 + data = 1 + buffer #29: + time = 1000010717378 + data = 1 + buffer #30: + time = 1000010740598 + data = 1 + buffer #31: + time = 1000010763818 + data = 1 + buffer #32: + time = 1000010787038 + data = 1 + buffer #33: + time = 1000010810258 + data = 1 + buffer #34: + time = 1000010833478 + data = 1 + buffer #35: + time = 1000010856698 + data = 1 + buffer #36: + time = 1000010879918 + data = 1 + buffer #37: + time = 1000010903138 + data = 1 + buffer #38: + time = 1000010926358 + data = 1 + buffer #39: + time = 1000010949578 + data = 1 + buffer #40: + time = 1000010972798 + data = 1 + buffer #41: + time = 1000010996018 + data = 1 + buffer #42: + time = 1000011019238 + data = 1 + buffer #43: + time = 1000011042458 + data = 1 + buffer #44: + time = 1000011065678 + data = 1 +ImageOutput: + rendered image count = 2 + image output #1: + presentationTimeUs = 0 + bitmap hash = -389047680 + image output #2: + presentationTimeUs = 0 + bitmap hash = -389047680