From f1b8af5a481e45968602e154556bc71acb661616 Mon Sep 17 00:00:00 2001 From: Kyle Finlinson <5882840+KyleFin@users.noreply.github.com> Date: Sat, 2 Apr 2022 00:22:42 -0600 Subject: [PATCH 1/7] video_player_android parts of rotationCorrection fix --- .../video_player_android/CHANGELOG.md | 4 ++++ .../plugins/videoplayer/VideoPlayer.java | 8 +++++++ .../lib/src/android_video_player.dart | 1 + .../video_player_android/pubspec.yaml | 4 ++-- .../test/android_video_player_test.dart | 21 +++++++++++++++++++ 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 1f0f06f739b2..95efe6997c35 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.3 + +* Sets rotationCorrection for videos recorded in landscapeRight (https://github.com/flutter/flutter/issues/60327). + ## 2.3.2 * Updates ExoPlayer to 2.17.0. 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 dc7c88144583..5ec157f33be7 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 @@ -282,6 +282,14 @@ private void sendInitialized() { } event.put("width", width); event.put("height", height); + + // Rotating the video with ExoPlayer does not seem to be possible with a Surface, + // so inform the Flutter code that the widget needs to be rotated to prevent + // upside-down playback for videos with rotationDegrees of 180 (other orientations work + // correctly without correction). + if (rotationDegrees == 180) { + event.put("rotationCorrection", rotationDegrees); + } } eventSink.success(event); } diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart index 31d0744e51dc..44422495a615 100644 --- a/packages/video_player/video_player_android/lib/src/android_video_player.dart +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -131,6 +131,7 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { duration: Duration(milliseconds: map['duration'] as int), size: Size((map['width'] as num?)?.toDouble() ?? 0.0, (map['height'] as num?)?.toDouble() ?? 0.0), + rotationCorrection: map['rotationCorrection'] as int? ?? 0, ); case 'completed': return VideoEvent( diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index aa288ed71eac..dddf4523331d 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/plugins/tree/master/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.3.2 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" @@ -20,7 +20,7 @@ flutter: dependencies: flutter: sdk: flutter - video_player_platform_interface: ">=4.2.0 <6.0.0" + video_player_platform_interface: ">=5.1.1 <6.0.0" dev_dependencies: flutter_test: diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart index b7cf763e16a6..aad31e4d83cc 100644 --- a/packages/video_player/video_player_android/test/android_video_player_test.dart +++ b/packages/video_player/video_player_android/test/android_video_player_test.dart @@ -253,6 +253,20 @@ void main() { }), (ByteData? data) {}); + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'initialized', + 'duration': 98765, + 'width': 1920, + 'height': 1080, + 'rotationCorrection': 180, + }), + (ByteData? data) {}); + await _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .handlePlatformMessage( @@ -312,6 +326,13 @@ void main() { eventType: VideoEventType.initialized, duration: const Duration(milliseconds: 98765), size: const Size(1920, 1080), + rotationCorrection: 0, + ), + VideoEvent( + eventType: VideoEventType.initialized, + duration: const Duration(milliseconds: 98765), + size: const Size(1920, 1080), + rotationCorrection: 180, ), VideoEvent(eventType: VideoEventType.completed), VideoEvent( From d1985bea28e884847c532ab23187a494987738da Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 10 May 2022 16:27:39 -0700 Subject: [PATCH 2/7] Copy over test and video player changes --- .../plugins/videoplayer/VideoPlayer.java | 37 ++++- .../videoplayer/VideoPlayerPluginTest.java | 15 ++ .../plugins/videoplayer/VideoPlayerTest.java | 148 +++++++++++++++++- 3 files changed, 189 insertions(+), 11 deletions(-) create mode 100644 packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java 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 5ec157f33be7..f215354cb929 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 @@ -11,6 +11,7 @@ import android.net.Uri; import android.view.Surface; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; @@ -51,11 +52,11 @@ final class VideoPlayer { private final TextureRegistry.SurfaceTextureEntry textureEntry; - private QueuingEventSink eventSink = new QueuingEventSink(); + private QueuingEventSink eventSink; private final EventChannel eventChannel; - private boolean isInitialized = false; + @VisibleForTesting boolean isInitialized = false; private final VideoPlayerOptions options; @@ -71,10 +72,11 @@ final class VideoPlayer { this.textureEntry = textureEntry; this.options = options; - exoPlayer = new ExoPlayer.Builder(context).build(); + ExoPlayer exoPlayer = new ExoPlayer.Builder(context).build(); Uri uri = Uri.parse(dataSource); DataSource.Factory dataSourceFactory; + if (isHTTP(uri)) { DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory() @@ -90,10 +92,26 @@ final class VideoPlayer { } MediaSource mediaSource = buildMediaSource(uri, dataSourceFactory, formatHint, context); + exoPlayer.setMediaSource(mediaSource); exoPlayer.prepare(); - setupVideoPlayer(eventChannel, textureEntry); + setUpVideoPlayer(exoPlayer, new QueuingEventSink()); + } + + // Constructor used to directly test members of this class. + @VisibleForTesting + VideoPlayer( + ExoPlayer exoPlayer, + EventChannel eventChannel, + TextureRegistry.SurfaceTextureEntry textureEntry, + VideoPlayerOptions options, + QueuingEventSink eventSink) { + this.eventChannel = eventChannel; + this.textureEntry = textureEntry; + this.options = options; + + setUpVideoPlayer(exoPlayer, eventSink); } private static boolean isHTTP(Uri uri) { @@ -106,7 +124,6 @@ private static boolean isHTTP(Uri uri) { private MediaSource buildMediaSource( Uri uri, DataSource.Factory mediaDataSourceFactory, String formatHint, Context context) { - int type; if (formatHint == null) { type = Util.inferContentType(uri.getLastPathSegment()); @@ -153,8 +170,10 @@ private MediaSource buildMediaSource( } } - private void setupVideoPlayer( - EventChannel eventChannel, TextureRegistry.SurfaceTextureEntry textureEntry) { + private void setUpVideoPlayer(ExoPlayer exoPlayer, QueuingEventSink eventSink) { + this.exoPlayer = exoPlayer; + this.eventSink = eventSink; + eventChannel.setStreamHandler( new EventChannel.StreamHandler() { @Override @@ -264,7 +283,8 @@ long getPosition() { } @SuppressWarnings("SuspiciousNameCombination") - private void sendInitialized() { + @VisibleForTesting + void sendInitialized() { if (isInitialized) { Map event = new HashMap<>(); event.put("event", "initialized"); @@ -291,6 +311,7 @@ private void sendInitialized() { event.put("rotationCorrection", rotationDegrees); } } + eventSink.success(event); } } diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java new file mode 100644 index 000000000000..2ed11653a4b8 --- /dev/null +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.videoplayer; + +import org.junit.Test; + +public class VideoPlayerPluginTest { + // This is only a placeholder test and doesn't actually initialize the plugin. + @Test + public void initPluginDoesNotThrow() { + final VideoPlayerPlugin plugin = new VideoPlayerPlugin(); + } +} 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 ec960b7a4480..194f7905b63a 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 @@ -4,12 +4,154 @@ package io.flutter.plugins.videoplayer; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.Format; +import io.flutter.plugin.common.EventChannel; +import io.flutter.view.TextureRegistry; +import java.util.HashMap; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +@RunWith(RobolectricTestRunner.class) public class VideoPlayerTest { - // This is only a placeholder test and doesn't actually initialize the plugin. + private ExoPlayer fakeExoPlayer; + private EventChannel fakeEventChannel; + private TextureRegistry.SurfaceTextureEntry fakeSurfaceTextureEntry; + private VideoPlayerOptions fakeVideoPlayerOptions; + private QueuingEventSink fakeEventSink; + + @Captor private ArgumentCaptor> eventCaptor; + + @Before + public void before() { + MockitoAnnotations.openMocks(this); + + fakeExoPlayer = mock(ExoPlayer.class); + fakeEventChannel = mock(EventChannel.class); + fakeSurfaceTextureEntry = mock(TextureRegistry.SurfaceTextureEntry.class); + fakeVideoPlayerOptions = mock(VideoPlayerOptions.class); + fakeEventSink = mock(QueuingEventSink.class); + } + + @Test + public void sendInitializedSendsExpectedEvent_90RotationDegrees() { + VideoPlayer videoPlayer = + new VideoPlayer( + fakeExoPlayer, + fakeEventChannel, + fakeSurfaceTextureEntry, + fakeVideoPlayerOptions, + fakeEventSink); + Format testFormat = + new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(90).build(); + + when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getDuration()).thenReturn(10L); + + videoPlayer.isInitialized = true; + videoPlayer.sendInitialized(); + + verify(fakeEventSink).success(eventCaptor.capture()); + HashMap event = eventCaptor.getValue(); + + assertEquals(event.get("event"), "initialized"); + assertEquals(event.get("duration"), 10L); + assertEquals(event.get("width"), 200); + assertEquals(event.get("height"), 100); + assertEquals(event.get("rotationCorrection"), null); + } + @Test - public void initPluginDoesNotThrow() { - final VideoPlayerPlugin plugin = new VideoPlayerPlugin(); + public void sendInitializedSendsExpectedEvent_270RotationDegrees() { + VideoPlayer videoPlayer = + new VideoPlayer( + fakeExoPlayer, + fakeEventChannel, + fakeSurfaceTextureEntry, + fakeVideoPlayerOptions, + fakeEventSink); + Format testFormat = + new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(270).build(); + + when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getDuration()).thenReturn(10L); + + videoPlayer.isInitialized = true; + videoPlayer.sendInitialized(); + + verify(fakeEventSink).success(eventCaptor.capture()); + HashMap event = eventCaptor.getValue(); + + assertEquals(event.get("event"), "initialized"); + assertEquals(event.get("duration"), 10L); + assertEquals(event.get("width"), 200); + assertEquals(event.get("height"), 100); + assertEquals(event.get("rotationCorrection"), null); + } + + @Test + public void sendInitializedSendsExpectedEvent_0RotationDegrees() { + VideoPlayer videoPlayer = + new VideoPlayer( + fakeExoPlayer, + fakeEventChannel, + fakeSurfaceTextureEntry, + fakeVideoPlayerOptions, + fakeEventSink); + Format testFormat = + new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(0).build(); + + when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getDuration()).thenReturn(10L); + + videoPlayer.isInitialized = true; + videoPlayer.sendInitialized(); + + verify(fakeEventSink).success(eventCaptor.capture()); + HashMap event = eventCaptor.getValue(); + + assertEquals(event.get("event"), "initialized"); + assertEquals(event.get("duration"), 10L); + assertEquals(event.get("width"), 100); + assertEquals(event.get("height"), 200); + assertEquals(event.get("rotationCorrection"), null); + } + + @Test + public void sendInitializedSendsExpectedEvent_180RotationDegrees() { + VideoPlayer videoPlayer = + new VideoPlayer( + fakeExoPlayer, + fakeEventChannel, + fakeSurfaceTextureEntry, + fakeVideoPlayerOptions, + fakeEventSink); + Format testFormat = + new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(180).build(); + + when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getDuration()).thenReturn(10L); + + videoPlayer.isInitialized = true; + videoPlayer.sendInitialized(); + + verify(fakeEventSink).success(eventCaptor.capture()); + HashMap event = eventCaptor.getValue(); + + assertEquals(event.get("event"), "initialized"); + assertEquals(event.get("duration"), 10L); + assertEquals(event.get("width"), 100); + assertEquals(event.get("height"), 200); + assertEquals(event.get("rotationCorrection"), 180); } } From dc17ed45b4d00be42bc19d1fe757d1b69e90d03c Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 12 May 2022 10:28:59 -0700 Subject: [PATCH 3/7] Move gradle changes --- .../video_player/video_player_android/android/build.gradle | 4 +++- packages/video_player/video_player_android/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index e565a9364bd8..020b5acf49da 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -48,7 +48,9 @@ android { implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.0' implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.0' testImplementation 'junit:junit:4.12' + testImplementation 'androidx.test:core:1.3.0' testImplementation 'org.mockito:mockito-inline:3.9.0' + testImplementation 'org.robolectric:robolectric:4.5' } @@ -63,4 +65,4 @@ android { } } } -} +} \ No newline at end of file diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index dddf4523331d..6a00668e7c18 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -20,7 +20,7 @@ flutter: dependencies: flutter: sdk: flutter - video_player_platform_interface: ">=5.1.1 <6.0.0" + video_player_platform_interface: ^5.1.1 dev_dependencies: flutter_test: From cbb9f3a900d8f01614f659f240589a2be69be6bc Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 25 May 2022 13:46:37 -0700 Subject: [PATCH 4/7] Revert version --- packages/video_player/video_player_android/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index c3c53c1ddd7f..6a00668e7c18 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/plugins/tree/master/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.3.4 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" From 6aab0deeb6c595344722e54a27b8c9fa76162493 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 25 May 2022 13:46:49 -0700 Subject: [PATCH 5/7] Re add version --- packages/video_player/video_player_android/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 6a00668e7c18..c3c53c1ddd7f 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/plugins/tree/master/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.3.3 +version: 2.3.4 environment: sdk: ">=2.14.0 <3.0.0" From ab42c88ea4e4bf6296b90f8ff7f2edcd6eafcd5d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 31 May 2022 12:52:22 -0400 Subject: [PATCH 6/7] Re-bump version --- packages/video_player/video_player_android/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index c3c53c1ddd7f..367b95ea5a60 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/plugins/tree/master/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.3.4 +version: 2.3.5 environment: sdk: ">=2.14.0 <3.0.0" From 8193bd27200527af5d36ca6054e08eec37c886b7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 31 May 2022 12:52:43 -0400 Subject: [PATCH 7/7] Update changelog version --- packages/video_player/video_player_android/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 851c1ae6917f..d5acda4cce22 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,7 +1,10 @@ +## 2.3.5 + +* Sets rotationCorrection for videos recorded in landscapeRight (https://github.com/flutter/flutter/issues/60327). + ## 2.3.4 * Updates ExoPlayer to 2.17.1. -* Sets rotationCorrection for videos recorded in landscapeRight (https://github.com/flutter/flutter/issues/60327). ## 2.3.3