From 07085f4e31b5dfb9d12ab54ce7c5890016ddb575 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Thu, 17 Apr 2025 10:32:45 -0700 Subject: [PATCH 1/4] Add core changes --- .../video_player/video_player_android/CHANGELOG.md | 5 +++++ .../texture/TextureExoPlayerEventListener.java | 14 +++++++------- .../videoplayer/texture/TextureVideoPlayer.java | 3 ++- .../videoplayer/TextureVideoPlayerTest.java | 1 + .../video_player/video_player_android/pubspec.yaml | 2 +- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index f9a2064447e4..c0409d9f8f26 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.8.3 + +* Changes plugin to use `TextureRegistry.SurfaceProducer.handlesCropAndRotation` to detect + whether or not the video player rotation needs to be corrected. + ## 2.8.2 * Fixes a [bug](https://github.com/flutter/flutter/issues/164689) that can cause video to diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureExoPlayerEventListener.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureExoPlayerEventListener.java index 3722ddebeece..37aadb3f02e8 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureExoPlayerEventListener.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureExoPlayerEventListener.java @@ -16,15 +16,18 @@ import java.util.Objects; public final class TextureExoPlayerEventListener extends ExoPlayerEventListener { + private boolean surfaceProducerHandlesCropAndRotation; + @VisibleForTesting public TextureExoPlayerEventListener( - @NonNull ExoPlayer exoPlayer, @NonNull VideoPlayerCallbacks events) { - this(exoPlayer, events, false); + @NonNull ExoPlayer exoPlayer, @NonNull VideoPlayerCallbacks events, boolean surfaceProducerHandlesCropAndRotation) { + this(exoPlayer, events, false, boolean surfaceProducerHandlesCropAndRotation); } public TextureExoPlayerEventListener( - @NonNull ExoPlayer exoPlayer, @NonNull VideoPlayerCallbacks events, boolean initialized) { + @NonNull ExoPlayer exoPlayer, @NonNull VideoPlayerCallbacks events, boolean surfaceProducerHandlesCropAndRotation, boolean initialized) { super(exoPlayer, events, initialized); + this.surfaceProducerHandlesCropAndRotation = surfaceProducerHandlesCropAndRotation; } @Override @@ -51,10 +54,7 @@ protected void sendInitialized() { reportedRotationCorrection = RotationDegrees.ROTATE_0; rotationCorrection = 0; } - } - // TODO(camsim99): Replace this with a call to `handlesCropAndRotation` when it is - // available in stable. https://github.com/flutter/flutter/issues/157198 - else if (Build.VERSION.SDK_INT < 29) { + } else if (surfaceProducerHandlesCropAndRotation) { // When the SurfaceTexture backend for Impeller is used, the preview should already // be correctly rotated. rotationCorrection = 0; diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java index 535c302921d7..cf7678888ee6 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java @@ -79,8 +79,9 @@ public TextureVideoPlayer( @NonNull @Override protected ExoPlayerEventListener createExoPlayerEventListener(@NonNull ExoPlayer exoPlayer) { + boolean surfaceProducerHandlesCropAndRotation = surfaceProducer.handlesCropAndRotation(); return new TextureExoPlayerEventListener( - exoPlayer, videoPlayerEvents, playerHasBeenSuspended()); + exoPlayer, videoPlayerEvents, playerHasBeenSuspended(), surfaceProducerHandlesCropAndRotation); } @RestrictTo(RestrictTo.Scope.LIBRARY) diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureVideoPlayerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureVideoPlayerTest.java index 514a0d3a54ba..8755bad09531 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureVideoPlayerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureVideoPlayerTest.java @@ -55,6 +55,7 @@ public final class TextureVideoPlayerTest { public void setUp() { fakeVideoAsset = new FakeVideoAsset(FAKE_ASSET_URL); when(mockProducer.getSurface()).thenReturn(mock(Surface.class)); + when(mockProducer.handlesCropAndRotation()).thenReturn(true); } private VideoPlayer createVideoPlayer() { diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 40b0c33388c3..4c4293e130f9 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.8.2 +version: 2.8.3 environment: sdk: ^3.6.0 From ae754de8142809d51acc4ff9a8d881a5a9caf8e6 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Thu, 17 Apr 2025 10:33:57 -0700 Subject: [PATCH 2/4] add major test change --- .../TextureExoPlayerEventListenerTest.java | 59 +++++++++++++------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureExoPlayerEventListenerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureExoPlayerEventListenerTest.java index 931023b7a7b7..067230eef63c 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureExoPlayerEventListenerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureExoPlayerEventListenerTest.java @@ -12,7 +12,6 @@ import androidx.media3.common.VideoSize; import androidx.media3.exoplayer.ExoPlayer; import io.flutter.plugins.videoplayer.texture.TextureExoPlayerEventListener; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -33,18 +32,28 @@ public class TextureExoPlayerEventListenerTest { @Mock private ExoPlayer mockExoPlayer; @Mock private VideoPlayerCallbacks mockCallbacks; - private TextureExoPlayerEventListener eventListener; @Rule public MockitoRule initRule = MockitoJUnit.rule(); - @Before - public void setUp() { - eventListener = new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks); + @Test + @Config(maxSdk = 21) + public void onPlaybackStateChangedReadySendInitialized_belowAndroid21() { + TextureExoPlayerEventListener eventListener = + new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks, true); + VideoSize size = new VideoSize(800, 400, 0, 0); + when(mockExoPlayer.getVideoSize()).thenReturn(size); + when(mockExoPlayer.getDuration()).thenReturn(10L); + + eventListener.onPlaybackStateChanged(Player.STATE_READY); + verify(mockCallbacks).onInitialized(800, 400, 10L, 0); } @Test - @Config(maxSdk = 28) - public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { + @Config(minSdk = 22) + public void + onPlaybackStateChangedReadySendInitialized_whenSurfaceProducerHandlesCropAndRotation() { + TextureExoPlayerEventListener eventListener = + new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks, true); VideoSize size = new VideoSize(800, 400, 0, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); when(mockExoPlayer.getDuration()).thenReturn(10L); @@ -54,9 +63,11 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { } @Test - @Config(minSdk = 29) + @Config(minSdk = 22) public void - onPlaybackStateChangedReadySendInitializedWithRotationCorrectionAndWidthAndHeightSwap_aboveAndroid29() { + onPlaybackStateChangedReadySendInitializedWithRotationCorrectionAndWidthAndHeightSwap_whenSurfaceProducerDoesNotHandleCropAndRotation() { + TextureExoPlayerEventListener eventListener = + new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks, false); VideoSize size = new VideoSize(800, 400, 0, 0); int rotationCorrection = 90; Format videoFormat = new Format.Builder().setRotationDegrees(rotationCorrection).build(); @@ -73,6 +84,8 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Config(maxSdk = 21) public void onPlaybackStateChangedReadyInPortraitMode90DegreesSwapWidthAndHeight_belowAndroid21() { + TextureExoPlayerEventListener eventListener = + new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks, true); VideoSize size = new VideoSize(800, 400, 90, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); when(mockExoPlayer.getDuration()).thenReturn(10L); @@ -82,9 +95,11 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { } @Test - @Config(minSdk = 22, maxSdk = 28) + @Config(minSdk = 22) public void - onPlaybackStateChangedReadyInPortraitMode90DegreesDoesNotSwapWidthAndHeight_aboveAndroid21belowAndroid29() { + onPlaybackStateChangedReadyInPortraitMode90DegreesDoesNotSwapWidthAndHeight_whenSurfaceProducerHandlesCropAndRotation() { + TextureExoPlayerEventListener eventListener = + new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks, true); VideoSize size = new VideoSize(800, 400, 90, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); @@ -95,9 +110,11 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { } @Test - @Config(minSdk = 29) + @Config(minSdk = 22) public void - onPlaybackStateChangedReadyInPortraitMode90DegreesSwapWidthAndHeight_aboveAndroid29() { + onPlaybackStateChangedReadyInPortraitMode90DegreesSwapWidthAndHeight_whenSurfaceProducerDoesNotHandleCropAndRotation() { + TextureExoPlayerEventListener eventListener = + new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks, false); VideoSize size = new VideoSize(800, 400, 0, 0); int rotationCorrection = 90; Format videoFormat = new Format.Builder().setRotationDegrees(rotationCorrection).build(); @@ -114,6 +131,8 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Config(maxSdk = 21) public void onPlaybackStateChangedReadyInPortraitMode270DegreesSwapWidthAndHeight_belowAndroid21() { + TextureExoPlayerEventListener eventListener = + new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks, true); VideoSize size = new VideoSize(800, 400, 270, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); when(mockExoPlayer.getDuration()).thenReturn(10L); @@ -123,9 +142,11 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { } @Test - @Config(minSdk = 22, maxSdk = 28) + @Config(minSdk = 22) public void - onPlaybackStateChangedReadyInPortraitMode270DegreesDoesNotSwapWidthAndHeight_aboveAndroid21belowAndroid29() { + onPlaybackStateChangedReadyInPortraitMode270DegreesDoesNotSwapWidthAndHeight_whenSurfaceProducerHandlesCropAndRotation() { + TextureExoPlayerEventListener eventListener = + new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks, true); VideoSize size = new VideoSize(800, 400, 270, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); when(mockExoPlayer.getDuration()).thenReturn(10L); @@ -135,9 +156,11 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { } @Test - @Config(minSdk = 29) + @Config(minSdk = 22) public void - onPlaybackStateChangedReadyInPortraitMode270DegreesSwapWidthAndHeight_aboveAndroid29() { + onPlaybackStateChangedReadyInPortraitMode270DegreesDoesNotSwapWidthAndHeight_whenSurfaceProducerDoesNotHandleCropAndRotation() { + TextureExoPlayerEventListener eventListener = + new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks, false); VideoSize size = new VideoSize(800, 400, 0, 0); int rotationCorrection = 270; Format videoFormat = new Format.Builder().setRotationDegrees(rotationCorrection).build(); @@ -153,6 +176,8 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Test @Config(maxSdk = 21) public void onPlaybackStateChangedReadyFlipped180DegreesInformEventHandler_belowAndroid21() { + TextureExoPlayerEventListener eventListener = + new TextureExoPlayerEventListener(mockExoPlayer, mockCallbacks, true); VideoSize size = new VideoSize(800, 400, 180, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); when(mockExoPlayer.getDuration()).thenReturn(10L); From 7365b2e96af102427a39ac16d77bb49772d22d24 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Thu, 17 Apr 2025 12:58:44 -0700 Subject: [PATCH 3/4] fix logic + tests --- .../plugins/videoplayer/VideoPlayer.java | 10 +++++--- .../platformview/PlatformViewVideoPlayer.java | 9 ++++--- .../TextureExoPlayerEventListener.java | 11 +++++--- .../texture/TextureVideoPlayer.java | 25 +++++++++++-------- .../videoplayer/TextureVideoPlayerTest.java | 1 + .../plugins/videoplayer/VideoPlayerTest.java | 10 +++++--- 6 files changed, 44 insertions(+), 22 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java index 8040e86419c0..0b46bfe4e934 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java @@ -8,11 +8,13 @@ import static androidx.media3.common.Player.REPEAT_MODE_OFF; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; import androidx.media3.common.MediaItem; import androidx.media3.common.PlaybackParameters; import androidx.media3.exoplayer.ExoPlayer; +import io.flutter.view.TextureRegistry.SurfaceProducer; /** * A class responsible for managing video playback using {@link ExoPlayer}. @@ -24,6 +26,7 @@ public abstract class VideoPlayer { @NonNull private final MediaItem mediaItem; @NonNull private final VideoPlayerOptions options; @NonNull protected final VideoPlayerCallbacks videoPlayerEvents; + @Nullable protected final SurfaceProducer surfaceProducer; @NonNull protected ExoPlayer exoPlayer; /** A closure-compatible signature since {@link java.util.function.Supplier} is API level 24. */ @@ -41,11 +44,13 @@ public VideoPlayer( @NonNull VideoPlayerCallbacks events, @NonNull MediaItem mediaItem, @NonNull VideoPlayerOptions options, + @Nullable SurfaceProducer surfaceProducer, @NonNull ExoPlayerProvider exoPlayerProvider) { this.videoPlayerEvents = events; this.mediaItem = mediaItem; this.options = options; this.exoPlayerProvider = exoPlayerProvider; + this.surfaceProducer = surfaceProducer; this.exoPlayer = createVideoPlayer(); } @@ -54,8 +59,7 @@ protected ExoPlayer createVideoPlayer() { ExoPlayer exoPlayer = exoPlayerProvider.get(); exoPlayer.setMediaItem(mediaItem); exoPlayer.prepare(); - - exoPlayer.addListener(createExoPlayerEventListener(exoPlayer)); + exoPlayer.addListener(createExoPlayerEventListener(exoPlayer, surfaceProducer)); setAudioAttributes(exoPlayer, options.mixWithOthers); return exoPlayer; @@ -63,7 +67,7 @@ protected ExoPlayer createVideoPlayer() { @NonNull protected abstract ExoPlayerEventListener createExoPlayerEventListener( - @NonNull ExoPlayer exoPlayer); + @NonNull ExoPlayer exoPlayer, @Nullable SurfaceProducer surfaceProducer); void sendBufferingUpdate() { videoPlayerEvents.onBufferingUpdate(exoPlayer.getBufferedPosition()); diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/platformview/PlatformViewVideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/platformview/PlatformViewVideoPlayer.java index 144c942676c1..7d033ba67b36 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/platformview/PlatformViewVideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/platformview/PlatformViewVideoPlayer.java @@ -6,6 +6,7 @@ import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.media3.common.MediaItem; import androidx.media3.exoplayer.ExoPlayer; @@ -14,6 +15,7 @@ import io.flutter.plugins.videoplayer.VideoPlayer; import io.flutter.plugins.videoplayer.VideoPlayerCallbacks; import io.flutter.plugins.videoplayer.VideoPlayerOptions; +import io.flutter.view.TextureRegistry.SurfaceProducer; /** * A subclass of {@link VideoPlayer} that adds functionality related to platform view as a way of @@ -26,7 +28,7 @@ public PlatformViewVideoPlayer( @NonNull MediaItem mediaItem, @NonNull VideoPlayerOptions options, @NonNull ExoPlayerProvider exoPlayerProvider) { - super(events, mediaItem, options, exoPlayerProvider); + super(events, mediaItem, options, /* surfaceProducer */ null, exoPlayerProvider); } /** @@ -58,9 +60,10 @@ public static PlatformViewVideoPlayer create( @NonNull @Override - protected ExoPlayerEventListener createExoPlayerEventListener(@NonNull ExoPlayer exoPlayer) { + protected ExoPlayerEventListener createExoPlayerEventListener( + @NonNull ExoPlayer exoPlayer, @Nullable SurfaceProducer surfaceProducer) { // Platform view video player does not suspend and re-create the exoPlayer, hence initialized - // is always false. + // is always false. It also does not require a reference to the SurfaceProducer. return new PlatformViewExoPlayerEventListener(exoPlayer, videoPlayerEvents, false); } } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureExoPlayerEventListener.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureExoPlayerEventListener.java index 37aadb3f02e8..99b2fa07d06a 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureExoPlayerEventListener.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureExoPlayerEventListener.java @@ -20,12 +20,17 @@ public final class TextureExoPlayerEventListener extends ExoPlayerEventListener @VisibleForTesting public TextureExoPlayerEventListener( - @NonNull ExoPlayer exoPlayer, @NonNull VideoPlayerCallbacks events, boolean surfaceProducerHandlesCropAndRotation) { - this(exoPlayer, events, false, boolean surfaceProducerHandlesCropAndRotation); + @NonNull ExoPlayer exoPlayer, + @NonNull VideoPlayerCallbacks events, + boolean surfaceProducerHandlesCropAndRotation) { + this(exoPlayer, events, surfaceProducerHandlesCropAndRotation, false); } public TextureExoPlayerEventListener( - @NonNull ExoPlayer exoPlayer, @NonNull VideoPlayerCallbacks events, boolean surfaceProducerHandlesCropAndRotation, boolean initialized) { + @NonNull ExoPlayer exoPlayer, + @NonNull VideoPlayerCallbacks events, + boolean surfaceProducerHandlesCropAndRotation, + boolean initialized) { super(exoPlayer, events, initialized); this.surfaceProducerHandlesCropAndRotation = surfaceProducerHandlesCropAndRotation; } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java index cf7678888ee6..a2a7623d3e17 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java @@ -17,7 +17,7 @@ import io.flutter.plugins.videoplayer.VideoPlayer; import io.flutter.plugins.videoplayer.VideoPlayerCallbacks; import io.flutter.plugins.videoplayer.VideoPlayerOptions; -import io.flutter.view.TextureRegistry; +import io.flutter.view.TextureRegistry.SurfaceProducer; /** * A subclass of {@link VideoPlayer} that adds functionality related to texture view as a way of @@ -26,9 +26,7 @@ *

It manages the lifecycle of the texture and ensures that the video is properly displayed on * the texture. */ -public final class TextureVideoPlayer extends VideoPlayer - implements TextureRegistry.SurfaceProducer.Callback { - @NonNull private final TextureRegistry.SurfaceProducer surfaceProducer; +public final class TextureVideoPlayer extends VideoPlayer implements SurfaceProducer.Callback { @Nullable private ExoPlayerState savedStateDuring; /** @@ -45,7 +43,7 @@ public final class TextureVideoPlayer extends VideoPlayer public static TextureVideoPlayer create( @NonNull Context context, @NonNull VideoPlayerCallbacks events, - @NonNull TextureRegistry.SurfaceProducer surfaceProducer, + @NonNull SurfaceProducer surfaceProducer, @NonNull VideoAsset asset, @NonNull VideoPlayerOptions options) { return new TextureVideoPlayer( @@ -64,13 +62,12 @@ public static TextureVideoPlayer create( @VisibleForTesting public TextureVideoPlayer( @NonNull VideoPlayerCallbacks events, - @NonNull TextureRegistry.SurfaceProducer surfaceProducer, + @NonNull SurfaceProducer surfaceProducer, @NonNull MediaItem mediaItem, @NonNull VideoPlayerOptions options, @NonNull ExoPlayerProvider exoPlayerProvider) { - super(events, mediaItem, options, exoPlayerProvider); + super(events, mediaItem, options, surfaceProducer, exoPlayerProvider); - this.surfaceProducer = surfaceProducer; surfaceProducer.setCallback(this); this.exoPlayer.setVideoSurface(surfaceProducer.getSurface()); @@ -78,10 +75,18 @@ public TextureVideoPlayer( @NonNull @Override - protected ExoPlayerEventListener createExoPlayerEventListener(@NonNull ExoPlayer exoPlayer) { + protected ExoPlayerEventListener createExoPlayerEventListener( + @NonNull ExoPlayer exoPlayer, @Nullable SurfaceProducer surfaceProducer) { + if (surfaceProducer == null) { + throw new IllegalArgumentException( + "surfaceProducer cannot be null to create aan ExoPlayerEventListener for TextureVideoPlayer."); + } boolean surfaceProducerHandlesCropAndRotation = surfaceProducer.handlesCropAndRotation(); return new TextureExoPlayerEventListener( - exoPlayer, videoPlayerEvents, playerHasBeenSuspended(), surfaceProducerHandlesCropAndRotation); + exoPlayer, + videoPlayerEvents, + surfaceProducerHandlesCropAndRotation, + playerHasBeenSuspended()); } @RestrictTo(RestrictTo.Scope.LIBRARY) diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureVideoPlayerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureVideoPlayerTest.java index 8755bad09531..4bbc89d810bb 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureVideoPlayerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/TextureVideoPlayerTest.java @@ -189,6 +189,7 @@ public void onSurfaceAvailableWithoutDestroyDoesNotRecreate() { // Initially create the video player, which creates the initial surface. VideoPlayer videoPlayer = createVideoPlayer(); verify(mockProducer).getSurface(); + verify(mockProducer).handlesCropAndRotation(); // Capture the lifecycle events so we can simulate onSurfaceAvailable/Destroyed. verify(mockProducer).setCallback(callbackCaptor.capture()); diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java index b3f354b66192..0b663b16e28c 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java @@ -8,6 +8,7 @@ import static org.mockito.Mockito.*; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; import androidx.media3.common.Format; @@ -16,6 +17,7 @@ import androidx.media3.common.Player; import androidx.media3.exoplayer.ExoPlayer; import io.flutter.plugins.videoplayer.platformview.PlatformViewExoPlayerEventListener; +import io.flutter.view.TextureRegistry.SurfaceProducer; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -57,13 +59,15 @@ private TestVideoPlayer( @NonNull VideoPlayerCallbacks events, @NonNull MediaItem mediaItem, @NonNull VideoPlayerOptions options, + @Nullable SurfaceProducer surfaceProducer, @NonNull ExoPlayerProvider exoPlayerProvider) { - super(events, mediaItem, options, exoPlayerProvider); + super(events, mediaItem, options, surfaceProducer, exoPlayerProvider); } @NonNull @Override - protected ExoPlayerEventListener createExoPlayerEventListener(@NonNull ExoPlayer exoPlayer) { + protected ExoPlayerEventListener createExoPlayerEventListener( + @NonNull ExoPlayer exoPlayer, @Nullable SurfaceProducer surfaceProducer) { // Use platform view implementation for testing. return new PlatformViewExoPlayerEventListener(exoPlayer, mockEvents); } @@ -80,7 +84,7 @@ private VideoPlayer createVideoPlayer() { private VideoPlayer createVideoPlayer(VideoPlayerOptions options) { return new TestVideoPlayer( - mockEvents, fakeVideoAsset.getMediaItem(), options, () -> mockExoPlayer); + mockEvents, fakeVideoAsset.getMediaItem(), options, null, () -> mockExoPlayer); } @Test From b32181f32d8ff481cb1094cc7475470177a3edd3 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Mon, 5 May 2025 15:21:08 -0700 Subject: [PATCH 4/4] Update packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java Co-authored-by: Gray Mackall <34871572+gmackall@users.noreply.github.com> --- .../flutter/plugins/videoplayer/texture/TextureVideoPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java index a2a7623d3e17..2e8f1c789d91 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java @@ -79,7 +79,7 @@ protected ExoPlayerEventListener createExoPlayerEventListener( @NonNull ExoPlayer exoPlayer, @Nullable SurfaceProducer surfaceProducer) { if (surfaceProducer == null) { throw new IllegalArgumentException( - "surfaceProducer cannot be null to create aan ExoPlayerEventListener for TextureVideoPlayer."); + "surfaceProducer cannot be null to create an ExoPlayerEventListener for TextureVideoPlayer."); } boolean surfaceProducerHandlesCropAndRotation = surfaceProducer.handlesCropAndRotation(); return new TextureExoPlayerEventListener(