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
This commit is contained in:
kimvde 2025-04-22 08:41:27 -07:00 committed by Copybara-Service
parent ab6b0f6e10
commit fe10ca2c9a
4 changed files with 63 additions and 4 deletions

View File

@ -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();
}

View File

@ -115,6 +115,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
private VideoGraph.@MonotonicNonNull Factory videoGraphFactory;
private List<Effect> 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.
*
* <p>The default value is {@code false}.
*
* <p>This should only be set to {@code true} if there is a single {@linkplain
* PlaybackVideoGraphWrapper#getSink(int) input sink}.
*
* <p>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.
*
* <p>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<InputVideoSink> inputVideoSinks;
private final List<Effect> 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

View File

@ -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) {

View File

@ -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();