diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index bfefb9902768..8dca057f5a08 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.4.3 +* Fixes Android to correctly display videos recorded in landscapeRight (https://github.com/flutter/flutter/issues/60327). * Fixes order-dependent unit tests. ## 2.4.2 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index a6ec51015c33..95bdc07dc86c 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math' as math; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -48,6 +49,7 @@ class VideoPlayerValue { this.isBuffering = false, this.volume = 1.0, this.playbackSpeed = 1.0, + this.rotationCorrection = 0, this.errorDescription, }); @@ -111,6 +113,9 @@ class VideoPlayerValue { /// The [size] of the currently loaded video. final Size size; + /// Degrees to rotate the video (clockwise) so it is displayed correctly. + final int rotationCorrection; + /// Indicates whether or not the video has been loaded and is ready to play. final bool isInitialized; @@ -136,7 +141,7 @@ class VideoPlayerValue { } /// Returns a new instance that has the same values as this current instance, - /// except for any overrides passed in as arguments to [copyWidth]. + /// except for any overrides passed in as arguments to [copyWith]. VideoPlayerValue copyWith({ Duration? duration, Size? size, @@ -150,6 +155,7 @@ class VideoPlayerValue { bool? isBuffering, double? volume, double? playbackSpeed, + int? rotationCorrection, String? errorDescription = _defaultErrorDescription, }) { return VideoPlayerValue( @@ -165,6 +171,7 @@ class VideoPlayerValue { isBuffering: isBuffering ?? this.isBuffering, volume: volume ?? this.volume, playbackSpeed: playbackSpeed ?? this.playbackSpeed, + rotationCorrection: rotationCorrection ?? this.rotationCorrection, errorDescription: errorDescription != _defaultErrorDescription ? errorDescription : this.errorDescription, @@ -368,6 +375,7 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith( duration: event.duration, size: event.size, + rotationCorrection: event.rotationCorrection, isInitialized: event.duration != null, errorDescription: null, ); @@ -761,10 +769,29 @@ class _VideoPlayerState extends State { Widget build(BuildContext context) { return _textureId == VideoPlayerController.kUninitializedTextureId ? Container() - : _videoPlayerPlatform.buildView(_textureId); + : _VideoPlayerWithRotation( + rotation: widget.controller.value.rotationCorrection, + child: _videoPlayerPlatform.buildView(_textureId), + ); } } +class _VideoPlayerWithRotation extends StatelessWidget { + const _VideoPlayerWithRotation( + {Key? key, required this.rotation, required this.child}) + : super(key: key); + final int rotation; + final Widget child; + + @override + Widget build(BuildContext context) => rotation == 0 + ? child + : Transform.rotate( + angle: rotation * math.pi / 180, + child: child, + ); +} + /// Used to configure the [VideoProgressIndicator] widget's colors for how it /// describes the video's status. /// diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 05cfcf154f88..8b2fc5780e3d 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/main/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.4.2 +version: 2.4.3 environment: sdk: ">=2.14.0 <3.0.0" @@ -23,9 +23,9 @@ dependencies: flutter: sdk: flutter html: ^0.15.0 - video_player_android: ^2.2.17 + video_player_android: ^2.3.5 video_player_avfoundation: ^2.2.17 - video_player_platform_interface: ^5.1.0 + video_player_platform_interface: ^5.1.1 video_player_web: ^2.0.0 dev_dependencies: 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 83a49882ee9a..f4eda111fb92 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -15,6 +16,8 @@ class FakeController extends ValueNotifier implements VideoPlayerController { FakeController() : super(VideoPlayerValue(duration: Duration.zero)); + FakeController.value(VideoPlayerValue value) : super(value); + @override Future dispose() async { super.dispose(); @@ -149,6 +152,27 @@ void main() { findsOneWidget); }); + testWidgets('non-zero rotationCorrection value is used', + (WidgetTester tester) async { + final FakeController controller = FakeController.value( + VideoPlayerValue(duration: Duration.zero, rotationCorrection: 180)); + controller.textureId = 1; + await tester.pumpWidget(VideoPlayer(controller)); + final Transform actualRotationCorrection = + find.byType(Transform).evaluate().single.widget as Transform; + expect( + actualRotationCorrection.transform, equals(Matrix4.rotationZ(math.pi))); + }); + + testWidgets('no transform when rotationCorrection is zero', + (WidgetTester tester) async { + final FakeController controller = FakeController.value( + VideoPlayerValue(duration: Duration.zero, rotationCorrection: 0)); + controller.textureId = 1; + await tester.pumpWidget(VideoPlayer(controller)); + expect(find.byType(Transform), findsNothing); + }); + group('ClosedCaption widget', () { testWidgets('uses a default text style', (WidgetTester tester) async { const String text = 'foo';