diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java index b563676b81..e5bb66e70c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java @@ -139,6 +139,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } } + @Override + public void setPendingVideoEffects(List videoEffects) { + this.videoEffects = videoEffects; + if (isInitialized()) { + checkStateNotNull(videoSinkImpl).setPendingVideoEffects(videoEffects); + } + } + @Override public void setStreamOffsetUs(long streamOffsetUs) { checkStateNotNull(videoSinkImpl).setStreamOffsetUs(streamOffsetUs); @@ -477,11 +485,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; renderedFirstFrame = false; } - /** Sets the {@linkplain Effect video effects}. */ + /** Sets the {@linkplain Effect video effects} to apply immediately. */ public void setVideoEffects(List videoEffects) { + setPendingVideoEffects(videoEffects); + maybeRegisterInputStream(); + } + + /** + * Sets the {@linkplain Effect video effects} to apply when the next stream is {@linkplain + * #registerInputStream(int, Format) registered}. + */ + public void setPendingVideoEffects(List videoEffects) { this.videoEffects.clear(); this.videoEffects.addAll(videoEffects); - maybeRegisterInputStream(); } public void setStreamOffsetUs(long streamOffsetUs) { 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 8c45615ba3..323e1198b8 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 @@ -801,8 +801,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer implements Video case MSG_SET_VIDEO_EFFECTS: @SuppressWarnings("unchecked") List videoEffects = (List) checkNotNull(message); - videoSinkProvider.setVideoEffects(videoEffects); - hasEffects = true; + setVideoEffects(videoEffects); break; case MSG_SET_VIDEO_OUTPUT_RESOLUTION: Size outputResolution = (Size) checkNotNull(message); @@ -1130,6 +1129,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer implements Video hasInitializedPlayback = true; } + /** Sets the {@linkplain Effect video effects} to apply. */ + public void setVideoEffects(List effects) { + videoSinkProvider.setVideoEffects(effects); + hasEffects = true; + } + + protected final VideoSinkProvider getVideoSinkProvider() { + return videoSinkProvider; + } + @Override protected void onCodecInitialized( String name, @@ -1238,19 +1247,31 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer implements Video new VideoSize(width, height, unappliedRotationDegrees, pixelWidthHeightRatio); frameReleaseHelper.onFormatChanged(format.frameRate); - if (videoSink != null) { - videoSink.registerInputStream( - /* inputType= */ VideoSink.INPUT_TYPE_SURFACE, - format - .buildUpon() - .setWidth(width) - .setHeight(height) - .setRotationDegrees(unappliedRotationDegrees) - .setPixelWidthHeightRatio(pixelWidthHeightRatio) - .build()); + if (videoSink != null && mediaFormat != null) { + onReadyToRegisterVideoSinkInputStream(); + checkNotNull(videoSink) + .registerInputStream( + /* inputType= */ VideoSink.INPUT_TYPE_SURFACE, + format + .buildUpon() + .setWidth(width) + .setHeight(height) + .setRotationDegrees(unappliedRotationDegrees) + .setPixelWidthHeightRatio(pixelWidthHeightRatio) + .build()); } } + /** + * Called when ready to {@linkplain VideoSink#registerInputStream(int, Format) register} an input + * stream when {@linkplain #setVideoEffects video effects} are enabled. + * + *

The default implementation is a no-op. + */ + protected void onReadyToRegisterVideoSinkInputStream() { + // do nothing. + } + @Override @TargetApi(29) // codecHandlesHdr10PlusOutOfBandMetadata is false if Util.SDK_INT < 29 protected void handleInputBufferSupplementalData(DecoderInputBuffer buffer) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoSinkProvider.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoSinkProvider.java index d6bed7be6c..f29f548397 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoSinkProvider.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoSinkProvider.java @@ -20,10 +20,12 @@ import android.view.Surface; import androidx.media3.common.Effect; import androidx.media3.common.Format; import androidx.media3.common.util.Size; +import androidx.media3.common.util.UnstableApi; import java.util.List; /** A provider of {@link VideoSink VideoSinks}. */ -/* package */ interface VideoSinkProvider { +@UnstableApi +public interface VideoSinkProvider { /** * Initializes the provider for video frame processing. Can be called up to one time and only @@ -43,9 +45,15 @@ import java.util.List; /** Returns a {@link VideoSink} to forward video frames for processing. */ VideoSink getSink(); - /** Sets video effects on this provider. */ + /** Sets video effects on this provider to apply immediately. */ void setVideoEffects(List videoEffects); + /** + * Sets video effects on this provider to apply when the next stream is {@linkplain + * VideoSink#registerInputStream(int, Format) registered} on the {@link #getSink() VideoSink}. + */ + void setPendingVideoEffects(List videoEffects); + /** * Sets the offset, in microseconds, that is added to the video frames presentation timestamps * from the player.