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 7d9840f586..1765b6931b 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 @@ -201,7 +201,7 @@ public final class CompositingVideoSinkProvider private final PreviewingVideoGraph.Factory previewingVideoGraphFactory; private final CopyOnWriteArraySet listeners; - private Clock clock; + private @MonotonicNonNull Clock clock; private @MonotonicNonNull VideoFrameReleaseControl videoFrameReleaseControl; private @MonotonicNonNull VideoFrameRenderControl videoFrameRenderControl; private @MonotonicNonNull Format outputFormat; @@ -218,7 +218,6 @@ public final class CompositingVideoSinkProvider videoSinkImpl = new VideoSinkImpl(context); previewingVideoGraphFactory = checkStateNotNull(builder.previewingVideoGraphFactory); listeners = new CopyOnWriteArraySet<>(); - clock = Clock.DEFAULT; state = STATE_CREATED; playbackSpeed = 1f; addListener(videoSinkImpl); @@ -259,12 +258,6 @@ public final class CompositingVideoSinkProvider return videoFrameReleaseControl; } - @Override - public void setClock(Clock clock) { - checkState(!isInitialized()); - this.clock = clock; - } - @Override public void setVideoEffects(List videoEffects) { videoSinkImpl.setVideoEffects(videoEffects); @@ -382,7 +375,7 @@ public final class CompositingVideoSinkProvider Format format = outputFormat == null ? new Format.Builder().build() : outputFormat; videoFrameMetadataListener.onVideoFrameAboutToBeRendered( /* presentationTimeUs= */ presentationTimeUs - streamOffsetUs, - clock.nanoTime(), + checkStateNotNull(clock).nanoTime(), format, /* mediaFormat= */ null); } @@ -424,11 +417,12 @@ public final class CompositingVideoSinkProvider // Internal methods - private VideoFrameProcessor initialize(Format sourceFormat) throws VideoSink.VideoSinkException { + private VideoFrameProcessor initialize(Format sourceFormat, Clock clock) + throws VideoSink.VideoSinkException { checkState(state == STATE_CREATED); checkState(videoFrameRenderControl != null && videoFrameReleaseControl != null); - // Lazily initialize the handler here so it's initialized on the playback looper. + this.clock = clock; handler = clock.createHandler(checkStateNotNull(Looper.myLooper()), /* callback= */ null); ColorInfo inputColorInfo = getAdjustedInputColorInfo(sourceFormat.colorInfo); @@ -582,9 +576,9 @@ public final class CompositingVideoSinkProvider // VideoSink impl @Override - public void initialize(Format sourceFormat) throws VideoSinkException { + public void initialize(Format sourceFormat, Clock clock) throws VideoSinkException { checkState(!isInitialized()); - videoFrameProcessor = CompositingVideoSinkProvider.this.initialize(sourceFormat); + videoFrameProcessor = CompositingVideoSinkProvider.this.initialize(sourceFormat, clock); } @Override 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 3273bbf401..f9dd4a9643 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 @@ -53,7 +53,6 @@ import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.common.PlaybackException; import androidx.media3.common.VideoSize; -import androidx.media3.common.util.Clock; import androidx.media3.common.util.Log; import androidx.media3.common.util.MediaFormatUtil; import androidx.media3.common.util.Size; @@ -622,9 +621,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer @Override protected void onInit() { super.onInit(); - Clock clock = getClock(); - videoFrameReleaseControl.setClock(clock); - videoSinkProvider.setClock(clock); + videoFrameReleaseControl.setClock(getClock()); } @Override @@ -1073,7 +1070,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer videoSink = videoSinkProvider.getSink(); if (!videoSink.isInitialized()) { try { - videoSink.initialize(format); + videoSink.initialize(format, getClock()); } catch (VideoSink.VideoSinkException e) { throw createRendererException( e, format, PlaybackException.ERROR_CODE_VIDEO_FRAME_PROCESSOR_INIT_FAILED); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoSink.java index 59360b904d..bb8b29bae6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoSink.java @@ -24,6 +24,7 @@ import androidx.annotation.IntDef; import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.VideoSize; +import androidx.media3.common.util.Clock; import androidx.media3.common.util.TimestampIterator; import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; @@ -112,11 +113,12 @@ public interface VideoSink { * Initializes the video sink. * * @param sourceFormat The format of the compressed video. + * @param clock The {@link Clock} that should be used. * @throws VideoSink.VideoSinkException If initializing the sink failed. */ - void initialize(Format sourceFormat) throws VideoSinkException; + void initialize(Format sourceFormat, Clock clock) throws VideoSinkException; - /** Returns whether the video sink is {@linkplain #initialize(Format) initialized}. */ + /** Returns whether the video sink is {@linkplain #initialize(Format, Clock) initialized}. */ boolean isInitialized(); /** @@ -146,7 +148,7 @@ public interface VideoSink { /** * Returns the input {@link Surface} where the video sink consumes input frames from. * - *

Must be called after the sink is {@linkplain #initialize(Format) initialized}. + *

Must be called after the sink is {@linkplain #initialize(Format, Clock) initialized}. */ Surface getInputSurface(); @@ -159,7 +161,7 @@ public interface VideoSink { /** * Informs the video sink that a new input stream will be queued. * - *

Must be called after the sink is {@linkplain #initialize(Format) initialized}. + *

Must be called after the sink is {@linkplain #initialize(Format, Clock) initialized}. * * @param inputType The {@link InputType} of the stream. * @param format The {@link Format} of the stream. @@ -170,7 +172,7 @@ public interface VideoSink { * Informs the video sink that a frame will be queued to its {@linkplain #getInputSurface() input * surface}. * - *

Must be called after the sink is {@linkplain #initialize(Format) initialized}. + *

Must be called after the sink is {@linkplain #initialize(Format, Clock) initialized}. * * @param framePresentationTimeUs The frame's presentation time, in microseconds. * @param isLastFrame Whether this is the last frame of the video stream. @@ -183,7 +185,7 @@ public interface VideoSink { /** * Provides an input {@link Bitmap} to the video sink. * - *

Must be called after the sink is {@linkplain #initialize(Format) initialized}. + *

Must be called after the sink is {@linkplain #initialize(Format, Clock) initialized}. * * @param inputBitmap The {@link Bitmap} queued to the video sink. * @param timestampIterator The times within the current stream that the bitmap should be shown @@ -196,7 +198,7 @@ public interface VideoSink { /** * Incrementally renders processed video frames. * - *

Must be called after the sink is {@linkplain #initialize(Format) initialized}. + *

Must be called after the sink is {@linkplain #initialize(Format, Clock) initialized}. * * @param positionUs The current playback position, in microseconds. * @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds, 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 fe97e60bfc..e4bcab3acb 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 @@ -34,7 +34,7 @@ public interface VideoSinkProvider { * during rendering. * *

Must be called before the first {@linkplain #getSink() sink} is {@linkplain - * VideoSink#initialize(Format) initialized}. + * VideoSink#initialize(Format, Clock) initialized}. */ void setVideoFrameReleaseControl(VideoFrameReleaseControl videoFrameReleaseControl); @@ -47,14 +47,6 @@ public interface VideoSinkProvider { */ @Nullable VideoFrameReleaseControl getVideoFrameReleaseControl(); - /** - * Sets the {@link Clock} that the provider should use internally. - * - *

Must be called before the first {@linkplain #getSink() sink} is {@linkplain - * VideoSink#initialize(Format) initialized}. - */ - void setClock(Clock clock); - /** Sets video effects on this provider to apply immediately. */ void setVideoEffects(List videoEffects); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/CompositingVideoSinkProviderTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/CompositingVideoSinkProviderTest.java index 33e2b7ed21..09d7a66a9c 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/CompositingVideoSinkProviderTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/CompositingVideoSinkProviderTest.java @@ -28,6 +28,7 @@ import androidx.media3.common.Format; import androidx.media3.common.PreviewingVideoGraph; import androidx.media3.common.VideoFrameProcessor; import androidx.media3.common.VideoGraph; +import androidx.media3.common.util.Clock; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import java.util.List; @@ -60,16 +61,20 @@ public final class CompositingVideoSinkProviderTest { assertThrows( IllegalStateException.class, - () -> sink.initialize(new Format.Builder().setWidth(640).setHeight(480).build())); + () -> + sink.initialize( + new Format.Builder().setWidth(640).setHeight(480).build(), Clock.DEFAULT)); } @Test public void initializeSink_calledTwice_throws() throws VideoSink.VideoSinkException { CompositingVideoSinkProvider provider = createCompositingVideoSinkProvider(); VideoSink sink = provider.getSink(); - sink.initialize(new Format.Builder().build()); + sink.initialize(new Format.Builder().build(), Clock.DEFAULT); - assertThrows(IllegalStateException.class, () -> sink.initialize(new Format.Builder().build())); + assertThrows( + IllegalStateException.class, + () -> sink.initialize(new Format.Builder().build(), Clock.DEFAULT)); } @Test @@ -77,7 +82,7 @@ public final class CompositingVideoSinkProviderTest { throws VideoSink.VideoSinkException { CompositingVideoSinkProvider provider = createCompositingVideoSinkProvider(); VideoSink videoSink = provider.getSink(); - videoSink.initialize(new Format.Builder().build()); + videoSink.initialize(new Format.Builder().build(), Clock.DEFAULT); videoSink.registerInputStream( VideoSink.INPUT_TYPE_SURFACE, new Format.Builder().setWidth(640).setHeight(480).build());