diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index f07bb5f66f8c..9cb642a4db56 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.1.15 +* Ensured seekTo isn't called before video player is initialized. Fixes [#89259](https://github.com/flutter/flutter/issues/89259). * Updated Android lint settings. ## 2.1.14 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 772409258ac4..b4c4b2b2a311 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -415,14 +415,14 @@ class VideoPlayerController extends ValueNotifier { } Future _applyLooping() async { - if (!value.isInitialized || _isDisposed) { + if (_isDisposedOrNotInitialized) { return; } await _videoPlayerPlatform.setLooping(_textureId, value.isLooping); } Future _applyPlayPause() async { - if (!value.isInitialized || _isDisposed) { + if (_isDisposedOrNotInitialized) { return; } if (value.isPlaying) { @@ -455,14 +455,14 @@ class VideoPlayerController extends ValueNotifier { } Future _applyVolume() async { - if (!value.isInitialized || _isDisposed) { + if (_isDisposedOrNotInitialized) { return; } await _videoPlayerPlatform.setVolume(_textureId, value.volume); } Future _applyPlaybackSpeed() async { - if (!value.isInitialized || _isDisposed) { + if (_isDisposedOrNotInitialized) { return; } @@ -491,7 +491,7 @@ class VideoPlayerController extends ValueNotifier { /// If [moment] is outside of the video's full range it will be automatically /// and silently clamped. Future seekTo(Duration position) async { - if (_isDisposed) { + if (_isDisposedOrNotInitialized) { return; } if (position > value.duration) { @@ -572,6 +572,8 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith(position: position); value = value.copyWith(caption: _getCaptionAt(position)); } + + bool get _isDisposedOrNotInitialized => _isDisposed || !value.isInitialized; } class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 0d0cdb1cb436..7f6f608687cc 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.1.14 +version: 2.1.15 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index b5bfad605620..ad536f840c1d 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -318,6 +318,17 @@ void main() { expect(fakeVideoPlayerPlatform.calls.last, 'setPlaybackSpeed'); }); + test('play before initialized does not call platform', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + ); + expect(controller.value.isInitialized, isFalse); + + await controller.play(); + + expect(fakeVideoPlayerPlatform.calls, isEmpty); + }); + test('play restarts from beginning if video is at end', () async { final VideoPlayerController controller = VideoPlayerController.network( 'https://127.0.0.1', @@ -373,6 +384,17 @@ void main() { expect(await controller.position, const Duration(milliseconds: 500)); }); + test('before initialized does not call platform', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + ); + expect(controller.value.isInitialized, isFalse); + + await controller.seekTo(const Duration(milliseconds: 500)); + + expect(fakeVideoPlayerPlatform.calls, isEmpty); + }); + test('clamps values that are too high or low', () async { final VideoPlayerController controller = VideoPlayerController.network( 'https://127.0.0.1', diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 24631513f800..c42036511d96 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,11 @@ +## 4.2.1 + +* Added rotation on Android for videos recorded in landscapeRight Fixes [#60327](https://github.com/flutter/flutter/issues/60327). + +## 4.2.0 + +* Add `contentUri` to `DataSourceType`. + ## 4.1.0 * Add `httpHeaders` to `DataSource` @@ -29,7 +37,7 @@ ## 2.1.0 -* Add VideoPlayerOptions with audo mix mode +* Add VideoPlayerOptions with audio mix mode ## 2.0.2 diff --git a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart index e92e87013d68..f87ed92b3aa5 100644 --- a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart +++ b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart @@ -42,6 +42,9 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { case DataSourceType.file: message.uri = dataSource.uri; break; + case DataSourceType.contentUri: + message.uri = dataSource.uri; + break; } TextureMessage response = await _api.create(message); @@ -108,6 +111,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { duration: Duration(milliseconds: map['duration']), size: Size(map['width']?.toDouble() ?? 0.0, map['height']?.toDouble() ?? 0.0), + rotationCorrection: map['rotationCorrection']?.toDouble() ?? 0.0, ); case 'completed': return VideoEvent( diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index b2bff990401e..ae3c0709f7bf 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -196,6 +196,9 @@ enum DataSourceType { /// The video was loaded off of the local filesystem. file, + + /// The video is available via contentUri. Android only. + contentUri, } /// The file format of the given video. @@ -219,12 +222,13 @@ class VideoEvent { /// /// The [eventType] argument is required. /// - /// Depending on the [eventType], the [duration], [size] and [buffered] - /// arguments can be null. + /// Depending on the [eventType], the [duration], [size], + /// [rotationCorrection], and [buffered] arguments can be null. VideoEvent({ required this.eventType, this.duration, this.size, + this.rotationCorrection, this.buffered, }); @@ -241,6 +245,11 @@ class VideoEvent { /// Only used if [eventType] is [VideoEventType.initialized]. final Size? size; + /// Radians to rotate the video so it is displayed correctly. + /// + /// Only used if [eventType] is [VideoEventType.initialized]. + final double? rotationCorrection; + /// Buffered parts of the video. /// /// Only used if [eventType] is [VideoEventType.bufferingUpdate]. @@ -254,6 +263,7 @@ class VideoEvent { eventType == other.eventType && duration == other.duration && size == other.size && + rotationCorrection == other.rotationCorrection && listEquals(buffered, other.buffered); } @@ -262,6 +272,7 @@ class VideoEvent { eventType.hashCode ^ duration.hashCode ^ size.hashCode ^ + rotationCorrection.hashCode ^ buffered.hashCode; } diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index 2a0ef10a9d2b..79167bf7b233 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 4.1.0 +version: 4.2.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart index 9da71617e66a..04a67591c534 100644 --- a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart +++ b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart @@ -92,10 +92,12 @@ class _ApiLogger implements TestHostVideoPlayerApi { void main() { TestWidgetsFlutterBinding.ensureInitialized(); + // Store the initial instance before any tests change it. + final VideoPlayerPlatform initialInstance = VideoPlayerPlatform.instance; + group('$VideoPlayerPlatform', () { test('$MethodChannelVideoPlayer() is the default instance', () { - expect(VideoPlayerPlatform.instance, - isInstanceOf()); + expect(initialInstance, isInstanceOf()); }); }); @@ -253,6 +255,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': 3.14, + }), + (ByteData? data) {}); + await _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .handlePlatformMessage( @@ -312,6 +328,13 @@ void main() { eventType: VideoEventType.initialized, duration: const Duration(milliseconds: 98765), size: const Size(1920, 1080), + rotationCorrection: 0.0, + ), + VideoEvent( + eventType: VideoEventType.initialized, + duration: const Duration(milliseconds: 98765), + size: const Size(1920, 1080), + rotationCorrection: 3.14, ), VideoEvent(eventType: VideoEventType.completed), VideoEvent(