Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[video_player] Platform interface changes to fix Android rotation for videos recorded in landscapeRight #4632

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/video_player/video_player/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
12 changes: 7 additions & 5 deletions packages/video_player/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -415,14 +415,14 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
}

Future<void> _applyLooping() async {
if (!value.isInitialized || _isDisposed) {
if (_isDisposedOrNotInitialized) {
return;
}
await _videoPlayerPlatform.setLooping(_textureId, value.isLooping);
}

Future<void> _applyPlayPause() async {
if (!value.isInitialized || _isDisposed) {
if (_isDisposedOrNotInitialized) {
return;
}
if (value.isPlaying) {
Expand Down Expand Up @@ -455,14 +455,14 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
}

Future<void> _applyVolume() async {
if (!value.isInitialized || _isDisposed) {
if (_isDisposedOrNotInitialized) {
return;
}
await _videoPlayerPlatform.setVolume(_textureId, value.volume);
}

Future<void> _applyPlaybackSpeed() async {
if (!value.isInitialized || _isDisposed) {
if (_isDisposedOrNotInitialized) {
return;
}

Expand Down Expand Up @@ -491,7 +491,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
/// If [moment] is outside of the video's full range it will be automatically
/// and silently clamped.
Future<void> seekTo(Duration position) async {
if (_isDisposed) {
if (_isDisposedOrNotInitialized) {
return;
}
if (position > value.duration) {
Expand Down Expand Up @@ -572,6 +572,8 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
value = value.copyWith(position: position);
value = value.copyWith(caption: _getCaptionAt(position));
}

bool get _isDisposedOrNotInitialized => _isDisposed || !value.isInitialized;
}

class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver {
Expand Down
2 changes: 1 addition & 1 deletion packages/video_player/video_player/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
22 changes: 22 additions & 0 deletions packages/video_player/video_player/test/video_player_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
@@ -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`
Expand Down Expand Up @@ -29,7 +37,7 @@

## 2.1.0

* Add VideoPlayerOptions with audo mix mode
* Add VideoPlayerOptions with audio mix mode

## 2.0.2

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
});

Expand All @@ -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].
Expand All @@ -254,6 +263,7 @@ class VideoEvent {
eventType == other.eventType &&
duration == other.duration &&
size == other.size &&
rotationCorrection == other.rotationCorrection &&
listEquals(buffered, other.buffered);
}

Expand All @@ -262,6 +272,7 @@ class VideoEvent {
eventType.hashCode ^
duration.hashCode ^
size.hashCode ^
rotationCorrection.hashCode ^
buffered.hashCode;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<MethodChannelVideoPlayer>());
expect(initialInstance, isInstanceOf<MethodChannelVideoPlayer>());
});
});

Expand Down Expand Up @@ -253,6 +255,20 @@ void main() {
}),
(ByteData? data) {});

await _ambiguate(ServicesBinding.instance)
?.defaultBinaryMessenger
.handlePlatformMessage(
"flutter.io/videoPlayer/videoEvents123",
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'initialized',
'duration': 98765,
'width': 1920,
'height': 1080,
'rotationCorrection': 3.14,
}),
(ByteData? data) {});

await _ambiguate(ServicesBinding.instance)
?.defaultBinaryMessenger
.handlePlatformMessage(
Expand Down Expand Up @@ -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(
Expand Down