From fe10ca2c9a55aedac16deb0ea0be4a1ac22cd5af Mon Sep 17 00:00:00 2001 From: kimvde Date: Tue, 22 Apr 2025 08:41:27 -0700 Subject: [PATCH] Start and stop video rendering from CompositionPlayer Before, we were starting and stopping video rendering when the renderers were started/stopped. This doesn't work for multi-video sequences though because we shouldn't stop and start rendering at every MediaItem transition in any of the input sequences. PiperOrigin-RevId: 750206410 --- .../video/MediaCodecVideoRenderer.java | 1 + .../video/PlaybackVideoGraphWrapper.java | 51 ++++++++++++++++++- .../media3/transformer/CompositionPlayer.java | 1 + .../CompositionPlayerInternal.java | 14 ++++- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java index f7fb1936e4..c614ab2a4e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java @@ -983,6 +983,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer Context context, VideoFrameReleaseControl videoFrameReleaseControl) { // TODO: b/391109644 - Add a more explicit API to enable replaying. return new PlaybackVideoGraphWrapper.Builder(context, videoFrameReleaseControl) + .setEnablePlaylistMode(true) .setClock(getClock()) .build(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/PlaybackVideoGraphWrapper.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/PlaybackVideoGraphWrapper.java index 0a07a7662b..1f273c9eed 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/PlaybackVideoGraphWrapper.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/PlaybackVideoGraphWrapper.java @@ -115,6 +115,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video private VideoGraph.@MonotonicNonNull Factory videoGraphFactory; private List compositionEffects; private VideoCompositorSettings compositorSettings; + private boolean enablePlaylistMode; private Clock clock; private boolean requestOpenGlToneMapping; private boolean built; @@ -185,6 +186,36 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video return this; } + /** + * Sets whether to enable playlist mode. + * + *

The default value is {@code false}. + * + *

This should only be set to {@code true} if there is a single {@linkplain + * PlaybackVideoGraphWrapper#getSink(int) input sink}. + * + *

If {@code true}, the {@link VideoGraph} output is considered as a playlist of clips. In + * this case, {@link PlaybackVideoGraphWrapper#startRendering()} and {@link + * PlaybackVideoGraphWrapper#stopRendering()} are called internally, when the corresponding + * methods are caller on the sink. + * + *

If {@code false}, the {@link VideoGraph} output is considered as a single clip. In this + * case, the caller is responsible for calling {@link + * PlaybackVideoGraphWrapper#startRendering()} and {@link + * PlaybackVideoGraphWrapper#stopRendering()}. + * + * @param enablePlaylistMode Whether to enable playlist mode. + * @return This builder, for convenience. + */ + @CanIgnoreReturnValue + public Builder setEnablePlaylistMode(boolean enablePlaylistMode) { + // This is set to true for ExoPlayer.setVideoEffects(). It's always false in + // CompositionPlayer, even if the Composition has a single sequence, because CompositionPlayer + // shouldn't behave differently for single and multi-sequence. + this.enablePlaylistMode = enablePlaylistMode; + return this; + } + /** * Sets the {@link Clock} that will be used. * @@ -271,6 +302,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video private final SparseArray inputVideoSinks; private final List compositionEffects; private final VideoCompositorSettings compositorSettings; + private final boolean enablePlaylistMode; private final VideoSink defaultVideoSink; private final VideoSink.VideoFrameHandler videoFrameHandler; private final Clock clock; @@ -321,6 +353,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video inputVideoSinks = new SparseArray<>(); compositionEffects = builder.compositionEffects; compositorSettings = builder.compositorSettings; + enablePlaylistMode = builder.enablePlaylistMode; clock = builder.clock; defaultVideoSink = new DefaultVideoSink(builder.videoFrameReleaseControl, clock); videoFrameHandler = @@ -363,6 +396,16 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video listeners.remove(listener); } + /** Starts rendering to the output surface. */ + public void startRendering() { + defaultVideoSink.startRendering(); + } + + /** Stops rendering to the output surface. */ + public void stopRendering() { + defaultVideoSink.stopRendering(); + } + // VideoSinkProvider methods @Override @@ -704,12 +747,16 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video @Override public void startRendering() { - defaultVideoSink.startRendering(); + if (enablePlaylistMode) { + PlaybackVideoGraphWrapper.this.startRendering(); + } } @Override public void stopRendering() { - defaultVideoSink.stopRendering(); + if (enablePlaylistMode) { + PlaybackVideoGraphWrapper.this.stopRendering(); + } } @Override diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayer.java index d0385e2c9c..e2da470f72 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayer.java @@ -743,6 +743,7 @@ public final class CompositionPlayer extends SimpleBasePlayer } } else if (endedCount == players.size()) { playbackState = STATE_ENDED; + checkStateNotNull(compositionPlayerInternal).stopRendering(); } else { playbackState = STATE_READY; if (oldPlaybackState != STATE_READY && playWhenReady) { diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayerInternal.java b/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayerInternal.java index 07969e70e8..ff9fa8e262 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayerInternal.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayerInternal.java @@ -155,10 +155,10 @@ import androidx.media3.exoplayer.video.PlaybackVideoGraphWrapper; try { switch (message.what) { case MSG_START_RENDERING: - playbackAudioGraphWrapper.startRendering(); + startRenderingInternal(); break; case MSG_STOP_RENDERING: - playbackAudioGraphWrapper.stopRendering(); + stopRenderingInternal(); break; case MSG_SET_VOLUME: playbackAudioGraphWrapper.setVolume(/* volume= */ (float) message.obj); @@ -210,6 +210,16 @@ import androidx.media3.exoplayer.video.PlaybackVideoGraphWrapper; } } + public void startRenderingInternal() { + playbackAudioGraphWrapper.startRendering(); + playbackVideoGraphWrapper.startRendering(); + } + + public void stopRenderingInternal() { + playbackAudioGraphWrapper.stopRendering(); + playbackVideoGraphWrapper.stopRendering(); + } + private void clearOutputSurfaceInternal() { try { playbackVideoGraphWrapper.clearOutputSurfaceInfo();