From dd8ff99d732271ace1449c4fe5fa4e32052717d9 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Mon, 20 Feb 2023 10:48:37 +0100 Subject: [PATCH 01/11] track isPlaying state --- .../video_player/lib/video_player.dart | 6 ++++++ .../flutter/plugins/videoplayer/VideoPlayer.java | 10 ++++++++++ .../lib/src/android_video_player.dart | 5 +++++ .../ios/Classes/FLTVideoPlayerPlugin.m | 13 +++++++++++++ .../lib/src/avfoundation_video_player.dart | 5 +++++ .../lib/video_player_platform_interface.dart | 16 ++++++++++++++-- .../video_player_web/lib/src/video_player.dart | 14 ++++++++++++++ 7 files changed, 67 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 5720e2d9d136..2fbe2de2a74c 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -399,6 +399,12 @@ class VideoPlayerController extends ValueNotifier { case VideoEventType.bufferingEnd: value = value.copyWith(isBuffering: false); break; + case VideoEventType.playingUpdate: + // TODO(maRci002): make VideoPlayerController immutable and provide equals / hash + if (event.isPlaying! != value.isPlaying) { + value = value.copyWith(isPlaying: event.isPlaying); + } + break; case VideoEventType.unknown: break; } 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 e130c995aa2a..1e8c0a2dfa54 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 @@ -232,6 +232,16 @@ public void onPlayerError(final PlaybackException error) { eventSink.error("VideoError", "Video player had error " + error, null); } } + + @Override + public void onIsPlayingChanged(boolean isPlaying) { + if (eventSink != null) { + Map event = new HashMap<>(); + event.put("event", "playingUpdate"); + event.put("isPlaying", isPlaying); + 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 cee6d7d38f66..6ceaee484827 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 @@ -147,6 +147,11 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { return VideoEvent(eventType: VideoEventType.bufferingStart); case 'bufferingEnd': return VideoEvent(eventType: VideoEventType.bufferingEnd); + case 'playingUpdate': + return VideoEvent( + eventType: VideoEventType.playingUpdate, + isPlaying: map['isPlaying'] as bool, + ); default: return VideoEvent(eventType: VideoEventType.unknown); } diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index 3b066769621c..bd51e79e1bf7 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -62,6 +62,7 @@ - (instancetype)initWithURL:(NSURL *)url static void *playbackLikelyToKeepUpContext = &playbackLikelyToKeepUpContext; static void *playbackBufferEmptyContext = &playbackBufferEmptyContext; static void *playbackBufferFullContext = &playbackBufferFullContext; +static void *rateContext = &rateContext; @implementation FLTVideoPlayer - (instancetype)initWithAsset:(NSString *)asset frameUpdater:(FLTFrameUpdater *)frameUpdater { @@ -98,6 +99,10 @@ - (void)addObservers:(AVPlayerItem *)item { forKeyPath:@"playbackBufferFull" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:playbackBufferFullContext]; + [item addObserver:self + forKeyPath:@"rate" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:rateContext]; // Add an observer that will respond to itemDidPlayToEndTime [[NSNotificationCenter defaultCenter] addObserver:self @@ -317,6 +322,14 @@ - (void)observeValueForKeyPath:(NSString *)path if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingEnd"}); } + } else if (context == rateContext) { + AVPlayerItem *item = (AVPlayerItem *)object; + if (_eventSink != nil) { + _eventSink(@{ + @"event" : @"playingUpdate", + @"isPlaying" : item.rate > 0 ? @YES : @NO + }); + } } } diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index b5ebedda41e1..1c86bac9d405 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -146,6 +146,11 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { return VideoEvent(eventType: VideoEventType.bufferingStart); case 'bufferingEnd': return VideoEvent(eventType: VideoEventType.bufferingEnd); + case 'playingUpdate': + return VideoEvent( + eventType: VideoEventType.playingUpdate, + isPlaying: map['isPlaying'] as bool, + ); default: return VideoEvent(eventType: VideoEventType.unknown); } 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 d3df9b25df53..eab4436775fc 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 @@ -212,6 +212,7 @@ class VideoEvent { this.size, this.rotationCorrection, this.buffered, + this.isPlaying, }); /// The type of the event. @@ -237,6 +238,11 @@ class VideoEvent { /// Only used if [eventType] is [VideoEventType.bufferingUpdate]. final List? buffered; + /// Play state changed. + /// + /// Only used if [eventType] is [VideoEventType.playingUpdate]. + final bool? isPlaying; + @override bool operator ==(Object other) { return identical(this, other) || @@ -246,7 +252,8 @@ class VideoEvent { duration == other.duration && size == other.size && rotationCorrection == other.rotationCorrection && - listEquals(buffered, other.buffered); + listEquals(buffered, other.buffered) && + isPlaying == other.isPlaying; } @override @@ -256,13 +263,14 @@ class VideoEvent { size, rotationCorrection, buffered, + isPlaying, ); } /// Type of the event. /// /// Emitted by the platform implementation when the video is initialized or -/// completed or to communicate buffering events. +/// completed or to communicate buffering events or play state changed. enum VideoEventType { /// The video has been initialized. initialized, @@ -279,6 +287,10 @@ enum VideoEventType { /// The video stopped to buffer. bufferingEnd, + /// The video started to play (for instance by user or phone call ended) + /// or the video has been paused (for instance by user or phone call ended) + playingUpdate, + /// An unknown event has been received. unknown, } diff --git a/packages/video_player/video_player_web/lib/src/video_player.dart b/packages/video_player/video_player_web/lib/src/video_player.dart index 02ead1fdf93b..3c96a4d05467 100644 --- a/packages/video_player/video_player_web/lib/src/video_player.dart +++ b/packages/video_player/video_player_web/lib/src/video_player.dart @@ -102,6 +102,20 @@ class VideoPlayer { )); }); + _videoElement.onPlay.listen((dynamic _) { + _eventController.add(VideoEvent( + eventType: VideoEventType.playingUpdate, + isPlaying: true, + )); + }); + + _videoElement.onPause.listen((dynamic _) { + _eventController.add(VideoEvent( + eventType: VideoEventType.playingUpdate, + isPlaying: false, + )); + }); + _videoElement.onEnded.listen((dynamic _) { setBuffering(false); _eventController.add(VideoEvent(eventType: VideoEventType.completed)); From a05a759838a570a2226bf3aac63bd51e7b803782 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Mon, 20 Feb 2023 11:44:12 +0100 Subject: [PATCH 02/11] add dependency overrides for testing --- packages/video_player/video_player/pubspec.yaml | 11 +++++++++++ .../video_player_android/example/pubspec.yaml | 5 +++++ .../video_player/video_player_android/pubspec.yaml | 5 +++++ .../video_player_avfoundation/example/pubspec.yaml | 5 +++++ .../video_player_avfoundation/pubspec.yaml | 5 +++++ .../video_player_web/example/pubspec.yaml | 5 +++++ packages/video_player/video_player_web/pubspec.yaml | 5 +++++ 7 files changed, 41 insertions(+) diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index d75456ace469..b6d6fa36b6b2 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -31,3 +31,14 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + video_player_android: + path: ../video_player_android + video_player_avfoundation: + path: ../video_player_avfoundation + video_player_platform_interface: + path: ../video_player_platform_interface + video_player_web: + path: ../video_player_web \ No newline at end of file diff --git a/packages/video_player/video_player_android/example/pubspec.yaml b/packages/video_player/video_player_android/example/pubspec.yaml index 16ffe17e7ba3..e5249523af2c 100644 --- a/packages/video_player/video_player_android/example/pubspec.yaml +++ b/packages/video_player/video_player_android/example/pubspec.yaml @@ -33,3 +33,8 @@ flutter: assets: - assets/flutter-mark-square-64.png - assets/Butterfly-209.mp4 + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + video_player_platform_interface: + path: ../../video_player_platform_interface \ 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 3f46ec8a4d79..dd96d2a6bdd5 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -26,3 +26,8 @@ dev_dependencies: flutter_test: sdk: flutter pigeon: ^2.0.1 + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + video_player_platform_interface: + path: ../video_player_platform_interface \ No newline at end of file diff --git a/packages/video_player/video_player_avfoundation/example/pubspec.yaml b/packages/video_player/video_player_avfoundation/example/pubspec.yaml index 422fb91e35e5..5d43e02e6a06 100644 --- a/packages/video_player/video_player_avfoundation/example/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/example/pubspec.yaml @@ -33,3 +33,8 @@ flutter: assets: - assets/flutter-mark-square-64.png - assets/Butterfly-209.mp4 + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + video_player_platform_interface: + path: ../../video_player_platform_interface \ No newline at end of file diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index a5204137af20..a9c7a854532f 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -25,3 +25,8 @@ dev_dependencies: flutter_test: sdk: flutter pigeon: ^2.0.1 + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + video_player_platform_interface: + path: ../video_player_platform_interface \ No newline at end of file diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index c4de1ce54c1a..6a62152febaf 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -20,3 +20,8 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + video_player_platform_interface: + path: ../../video_player_platform_interface \ No newline at end of file diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 5e603034dd28..f62d13a30199 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -26,3 +26,8 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + video_player_platform_interface: + path: ../video_player_platform_interface \ No newline at end of file From 8cd2325480ea38bfea3aab7e1ab31a588bcfcb1e Mon Sep 17 00:00:00 2001 From: maRci002 Date: Mon, 20 Feb 2023 12:25:20 +0100 Subject: [PATCH 03/11] Read rate property from AVPlayer isntead of AVPlayerItem --- .../ios/Classes/FLTVideoPlayerPlugin.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index bd51e79e1bf7..e12ee1dc4ed2 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -70,7 +70,7 @@ - (instancetype)initWithAsset:(NSString *)asset frameUpdater:(FLTFrameUpdater *) return [self initWithURL:[NSURL fileURLWithPath:path] frameUpdater:frameUpdater httpHeaders:@{}]; } -- (void)addObservers:(AVPlayerItem *)item { +- (void)addObservers:(AVPlayerItem *)item player:(AVPlayer *)player { [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew @@ -99,7 +99,7 @@ - (void)addObservers:(AVPlayerItem *)item { forKeyPath:@"playbackBufferFull" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:playbackBufferFullContext]; - [item addObserver:self + [_player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:rateContext]; @@ -257,7 +257,7 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item [self createVideoOutputAndDisplayLink:frameUpdater]; - [self addObservers:item]; + [self addObservers:item player:_player]; [asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ] completionHandler:assetCompletionHandler]; @@ -323,11 +323,11 @@ - (void)observeValueForKeyPath:(NSString *)path _eventSink(@{@"event" : @"bufferingEnd"}); } } else if (context == rateContext) { - AVPlayerItem *item = (AVPlayerItem *)object; + AVPlayer *player = (AVPlayer *)object; if (_eventSink != nil) { _eventSink(@{ @"event" : @"playingUpdate", - @"isPlaying" : item.rate > 0 ? @YES : @NO + @"isPlaying" : player.rate > 0 ? @YES : @NO }); } } From 115474b5264d9755c82a2c97e35d6c88de90ff52 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Mon, 20 Feb 2023 13:41:19 +0100 Subject: [PATCH 04/11] format --- .../ios/Classes/FLTVideoPlayerPlugin.m | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index e12ee1dc4ed2..e0004c327992 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -100,9 +100,9 @@ - (void)addObservers:(AVPlayerItem *)item player:(AVPlayer *)player { options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:playbackBufferFullContext]; [_player addObserver:self - forKeyPath:@"rate" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:rateContext]; + forKeyPath:@"rate" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:rateContext]; // Add an observer that will respond to itemDidPlayToEndTime [[NSNotificationCenter defaultCenter] addObserver:self @@ -325,10 +325,7 @@ - (void)observeValueForKeyPath:(NSString *)path } else if (context == rateContext) { AVPlayer *player = (AVPlayer *)object; if (_eventSink != nil) { - _eventSink(@{ - @"event" : @"playingUpdate", - @"isPlaying" : player.rate > 0 ? @YES : @NO - }); + _eventSink(@{@"event" : @"playingUpdate", @"isPlaying" : player.rate > 0 ? @YES : @NO}); } } } From 73d86053b5f15aeaf93e3641d2c5659719a565b4 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Mon, 20 Feb 2023 14:55:13 +0100 Subject: [PATCH 05/11] override video_player_platform_interface for example --- packages/video_player/video_player/example/pubspec.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index 0b30e9fb01e7..2e100859c1dd 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -37,3 +37,8 @@ flutter: - assets/bumble_bee_captions.srt - assets/bumble_bee_captions.vtt - assets/Audio.mp3 + +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + video_player_platform_interface: + path: ../../video_player_platform_interface \ No newline at end of file From 925c3bc7b73cec263eebe9a546a459a177f8ba86 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Mon, 20 Feb 2023 15:48:17 +0100 Subject: [PATCH 06/11] Update changelogs --- packages/video_player/video_player/CHANGELOG.md | 4 ++++ packages/video_player/video_player/pubspec.yaml | 2 +- packages/video_player/video_player_android/CHANGELOG.md | 4 ++++ packages/video_player/video_player_android/pubspec.yaml | 2 +- packages/video_player/video_player_avfoundation/CHANGELOG.md | 4 ++++ .../ios/Classes/FLTVideoPlayerPlugin.m | 2 +- packages/video_player/video_player_avfoundation/pubspec.yaml | 2 +- .../video_player_platform_interface/CHANGELOG.md | 5 +++++ .../video_player_platform_interface/pubspec.yaml | 2 +- packages/video_player/video_player_web/CHANGELOG.md | 4 ++++ packages/video_player/video_player_web/pubspec.yaml | 2 +- 11 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index eed3b6bc2346..7600d39b1ff0 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -2,6 +2,10 @@ * Updates minimum Flutter version to 3.0. +## 2.5.2 + +* Synchronize `VideoPlayerValue.isPlaying` with underlying video player. + ## 2.5.1 * Updates code for stricter lint checks. diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index b6d6fa36b6b2..8827a8c5a4a9 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.5.1 +version: 2.5.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 56024c4ba233..dcb0ed683907 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -2,6 +2,10 @@ * Updates minimum Flutter version to 3.0. +## 2.3.11 + +* Synchronize `VideoPlayerValue.isPlaying` with `ExoPlayer`. + ## 2.3.10 * Adds compatibilty with version 6.0 of the platform interface. diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index dd96d2a6bdd5..32f3f2f54e46 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/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.3.10 +version: 2.3.11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index b8564c0a2236..28fb37d3d7b3 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -2,6 +2,10 @@ * Updates minimum Flutter version to 3.0. +## 2.3.9 + +* Synchronize `VideoPlayerValue.isPlaying` with `AVPlayer`. + ## 2.3.8 * Adds compatibilty with version 6.0 of the platform interface. diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index e0004c327992..f04af41fed75 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -99,7 +99,7 @@ - (void)addObservers:(AVPlayerItem *)item player:(AVPlayer *)player { forKeyPath:@"playbackBufferFull" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:playbackBufferFullContext]; - [_player addObserver:self + [player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:rateContext]; diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index a9c7a854532f..6564fd1bdebf 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.8 +version: 2.3.9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index e1acbf578027..4658ff84e52f 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -2,6 +2,11 @@ * Updates minimum Flutter version to 3.0. +## 6.0.2 + +* Add the `VideoEventType.playingUpdate` event to track changes in play / pause status with the +underlying video player. + ## 6.0.1 * Fixes comment describing file URI construction. diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index 8c6a8f400bb2..6400218e8dfb 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/main/packages/video_player/v 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: 6.0.1 +version: 6.0.2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 42355439ce12..e276f6331ab3 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -2,6 +2,10 @@ * Updates minimum Flutter version to 3.0. +## 2.0.14 + +* Synchronize `VideoPlayerValue.isPlaying` with `VideoElement`. + ## 2.0.13 * Adds compatibilty with version 6.0 of the platform interface. diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index f62d13a30199..2b2d5f93e861 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.0.13 +version: 2.0.14 environment: sdk: ">=2.12.0 <3.0.0" From b85fe97913380850ba985c82c044800d42b7c69e Mon Sep 17 00:00:00 2001 From: maRci002 Date: Mon, 20 Feb 2023 17:39:20 +0100 Subject: [PATCH 07/11] update tests --- .../video_player/lib/video_player.dart | 2 +- .../example/lib/mini_controller.dart | 6 ++++++ .../example/lib/mini_controller.dart | 6 ++++++ .../ios/Classes/FLTVideoPlayerPlugin.m | 6 +++--- .../integration_test/video_player_web_test.dart | 16 +++++++++------- 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 2fbe2de2a74c..11226a1b5743 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -400,7 +400,7 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith(isBuffering: false); break; case VideoEventType.playingUpdate: - // TODO(maRci002): make VideoPlayerController immutable and provide equals / hash + // TODO(maRci002): make VideoPlayerValue immutable and provide equals / hash if (event.isPlaying! != value.isPlaying) { value = value.copyWith(isPlaying: event.isPlaying); } diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index fb79a77fb2cb..b1a47a6c3d5b 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -243,6 +243,12 @@ class MiniController extends ValueNotifier { case VideoEventType.bufferingEnd: value = value.copyWith(isBuffering: false); break; + case VideoEventType.playingUpdate: + // TODO(maRci002): make VideoPlayerValue immutable and provide equals / hash + if (event.isPlaying! != value.isPlaying) { + value = value.copyWith(isPlaying: event.isPlaying); + } + break; case VideoEventType.unknown: break; } diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index fb79a77fb2cb..b1a47a6c3d5b 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -243,6 +243,12 @@ class MiniController extends ValueNotifier { case VideoEventType.bufferingEnd: value = value.copyWith(isBuffering: false); break; + case VideoEventType.playingUpdate: + // TODO(maRci002): make VideoPlayerValue immutable and provide equals / hash + if (event.isPlaying! != value.isPlaying) { + value = value.copyWith(isPlaying: event.isPlaying); + } + break; case VideoEventType.unknown: break; } diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index f04af41fed75..bd421cc07953 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -100,9 +100,9 @@ - (void)addObservers:(AVPlayerItem *)item player:(AVPlayer *)player { options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:playbackBufferFullContext]; [player addObserver:self - forKeyPath:@"rate" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:rateContext]; + forKeyPath:@"rate" + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:rateContext]; // Add an observer that will respond to itemDidPlayToEndTime [[NSNotificationCenter defaultCenter] addObserver:self diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart index 5053ea6e5b04..b7eee29f9951 100644 --- a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart @@ -175,7 +175,7 @@ void main() { VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId); final Future> stream = eventStream.timeout( - const Duration(seconds: 1), + const Duration(seconds: 2), onTimeout: (EventSink sink) { sink.close(); }, @@ -184,23 +184,25 @@ void main() { await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0); await VideoPlayerPlatform.instance.play(videoPlayerId); - // Let the video play, until we stop seeing events for a second + // Let the video play, until we stop seeing events for two seconds final List events = await stream; await VideoPlayerPlatform.instance.pause(videoPlayerId); // The expected list of event types should look like this: - // 1. bufferingStart, - // 2. bufferingUpdate (videoElement.onWaiting), - // 3. initialized (videoElement.onCanPlay), - // 4. bufferingEnd (videoElement.onCanPlayThrough), + // 1. playingUpdate (videoElement.onPlaying) + // 2. bufferingStart, + // 3. bufferingUpdate (videoElement.onWaiting), + // 4. initialized (videoElement.onCanPlay), + // 5. bufferingEnd (videoElement.onCanPlayThrough), expect( events.map((VideoEvent e) => e.eventType), equals([ + VideoEventType.playingUpdate, VideoEventType.bufferingStart, VideoEventType.bufferingUpdate, VideoEventType.initialized, - VideoEventType.bufferingEnd + VideoEventType.bufferingEnd, ])); }); }); From 29b39d0aac7fd91239bf8a73927aaba6212c75a4 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Mon, 20 Feb 2023 17:58:15 +0100 Subject: [PATCH 08/11] update changelog and authors --- packages/video_player/video_player/AUTHORS | 1 + packages/video_player/video_player/CHANGELOG.md | 5 +---- packages/video_player/video_player_android/AUTHORS | 1 + packages/video_player/video_player_android/CHANGELOG.md | 5 +---- packages/video_player/video_player_avfoundation/AUTHORS | 1 + packages/video_player/video_player_avfoundation/CHANGELOG.md | 5 +---- .../video_player/video_player_platform_interface/AUTHORS | 1 + .../video_player_platform_interface/CHANGELOG.md | 5 +---- packages/video_player/video_player_web/AUTHORS | 1 + packages/video_player/video_player_web/CHANGELOG.md | 5 +---- 10 files changed, 10 insertions(+), 20 deletions(-) diff --git a/packages/video_player/video_player/AUTHORS b/packages/video_player/video_player/AUTHORS index 02a9c690f330..e57e00b47c84 100644 --- a/packages/video_player/video_player/AUTHORS +++ b/packages/video_player/video_player/AUTHORS @@ -65,3 +65,4 @@ Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> Koen Van Looveren +Márton Matuz diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 7600d39b1ff0..ef329099fb90 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,10 +1,7 @@ -## NEXT - -* Updates minimum Flutter version to 3.0. - ## 2.5.2 * Synchronize `VideoPlayerValue.isPlaying` with underlying video player. +* Updates minimum Flutter version to 3.0. ## 2.5.1 diff --git a/packages/video_player/video_player_android/AUTHORS b/packages/video_player/video_player_android/AUTHORS index 493a0b4ef9c2..fc16c35c4c25 100644 --- a/packages/video_player/video_player_android/AUTHORS +++ b/packages/video_player/video_player_android/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Márton Matuz diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index dcb0ed683907..d60c1ccb2946 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,10 +1,7 @@ -## NEXT - -* Updates minimum Flutter version to 3.0. - ## 2.3.11 * Synchronize `VideoPlayerValue.isPlaying` with `ExoPlayer`. +* Updates minimum Flutter version to 3.0. ## 2.3.10 diff --git a/packages/video_player/video_player_avfoundation/AUTHORS b/packages/video_player/video_player_avfoundation/AUTHORS index 493a0b4ef9c2..fc16c35c4c25 100644 --- a/packages/video_player/video_player_avfoundation/AUTHORS +++ b/packages/video_player/video_player_avfoundation/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Márton Matuz diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 28fb37d3d7b3..e74ca0442768 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,10 +1,7 @@ -## NEXT - -* Updates minimum Flutter version to 3.0. - ## 2.3.9 * Synchronize `VideoPlayerValue.isPlaying` with `AVPlayer`. +* Updates minimum Flutter version to 3.0. ## 2.3.8 diff --git a/packages/video_player/video_player_platform_interface/AUTHORS b/packages/video_player/video_player_platform_interface/AUTHORS index 493a0b4ef9c2..fc16c35c4c25 100644 --- a/packages/video_player/video_player_platform_interface/AUTHORS +++ b/packages/video_player/video_player_platform_interface/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Márton Matuz diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 4658ff84e52f..fb84b7a61a9f 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,11 +1,8 @@ -## NEXT - -* Updates minimum Flutter version to 3.0. - ## 6.0.2 * Add the `VideoEventType.playingUpdate` event to track changes in play / pause status with the underlying video player. +* Updates minimum Flutter version to 3.0. ## 6.0.1 diff --git a/packages/video_player/video_player_web/AUTHORS b/packages/video_player/video_player_web/AUTHORS index 493a0b4ef9c2..fc16c35c4c25 100644 --- a/packages/video_player/video_player_web/AUTHORS +++ b/packages/video_player/video_player_web/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Márton Matuz diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index e276f6331ab3..1ea7efbe94bb 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,10 +1,7 @@ -## NEXT - -* Updates minimum Flutter version to 3.0. - ## 2.0.14 * Synchronize `VideoPlayerValue.isPlaying` with `VideoElement`. +* Updates minimum Flutter version to 3.0. ## 2.0.13 From 0eb1a80c9c6eed2b528b4ab1b4482f1a2fcd37e4 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Mon, 20 Feb 2023 22:59:32 +0100 Subject: [PATCH 09/11] add test for web --- .../test/android_video_player_test.dart | 45 ++++++++++++++++--- .../test/avfoundation_video_player_test.dart | 43 +++++++++++++++--- .../video_player_web_test.dart | 33 ++++++++++++++ 3 files changed, 108 insertions(+), 13 deletions(-) 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 6aa24e5c1808..66fa31c921cf 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 @@ -234,10 +234,11 @@ void main() { }); test('videoEventsFor', () async { + const String mockChannel = 'flutter.io/videoPlayer/videoEvents123'; _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .setMockMessageHandler( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, (ByteData? message) async { final MethodCall methodCall = const StandardMethodCodec().decodeMethodCall(message); @@ -245,7 +246,7 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'initialized', @@ -258,7 +259,7 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'initialized', @@ -272,7 +273,7 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'completed', @@ -282,7 +283,7 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'bufferingUpdate', @@ -296,7 +297,7 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'bufferingStart', @@ -306,13 +307,35 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'bufferingEnd', }), (ByteData? data) {}); + await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! + .defaultBinaryMessenger + .handlePlatformMessage( + mockChannel, + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'playingUpdate', + 'isPlaying': true, + }), + (ByteData? data) {}); + + await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! + .defaultBinaryMessenger + .handlePlatformMessage( + mockChannel, + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'playingUpdate', + 'isPlaying': false, + }), + (ByteData? data) {}); + return const StandardMethodCodec().encodeSuccessEnvelope(null); } else if (methodCall.method == 'cancel') { return const StandardMethodCodec().encodeSuccessEnvelope(null); @@ -351,6 +374,14 @@ void main() { ]), VideoEvent(eventType: VideoEventType.bufferingStart), VideoEvent(eventType: VideoEventType.bufferingEnd), + VideoEvent( + eventType: VideoEventType.playingUpdate, + isPlaying: true, + ), + VideoEvent( + eventType: VideoEventType.playingUpdate, + isPlaying: false, + ), ])); }); }); diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index c01373f05424..f4084e1bef73 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -234,10 +234,11 @@ void main() { }); test('videoEventsFor', () async { + const String mockChannel = 'flutter.io/videoPlayer/videoEvents123'; _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .setMockMessageHandler( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, (ByteData? message) async { final MethodCall methodCall = const StandardMethodCodec().decodeMethodCall(message); @@ -245,7 +246,7 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'initialized', @@ -258,7 +259,7 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'completed', @@ -268,7 +269,7 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'bufferingUpdate', @@ -282,7 +283,7 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'bufferingStart', @@ -292,13 +293,35 @@ void main() { await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! .defaultBinaryMessenger .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', + mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'bufferingEnd', }), (ByteData? data) {}); + await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! + .defaultBinaryMessenger + .handlePlatformMessage( + mockChannel, + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'playingUpdate', + 'isPlaying': true, + }), + (ByteData? data) {}); + + await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! + .defaultBinaryMessenger + .handlePlatformMessage( + mockChannel, + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'playingUpdate', + 'isPlaying': false, + }), + (ByteData? data) {}); + return const StandardMethodCodec().encodeSuccessEnvelope(null); } else if (methodCall.method == 'cancel') { return const StandardMethodCodec().encodeSuccessEnvelope(null); @@ -330,6 +353,14 @@ void main() { ]), VideoEvent(eventType: VideoEventType.bufferingStart), VideoEvent(eventType: VideoEventType.bufferingEnd), + VideoEvent( + eventType: VideoEventType.playingUpdate, + isPlaying: true, + ), + VideoEvent( + eventType: VideoEventType.playingUpdate, + isPlaying: false, + ), ])); }); }); diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart index b7eee29f9951..6750da6ee69b 100644 --- a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart @@ -169,6 +169,39 @@ void main() { expect(VideoPlayerPlatform.instance.setMixWithOthers(false), completes); }); + testWidgets('double call to play emit singe playingUpdate', + (WidgetTester tester) async { + final int videoPlayerId = await textureId; + final Stream eventStream = + VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId); + + final Future> stream = eventStream.timeout( + const Duration(seconds: 1), + onTimeout: (EventSink sink) { + sink.close(); + }, + ).toList(); + + await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0); + await VideoPlayerPlatform.instance.play(videoPlayerId); + await VideoPlayerPlatform.instance.play(videoPlayerId); + + // Let the video play, until we stop seeing events for two seconds + final List events = await stream; + + await VideoPlayerPlatform.instance.pause(videoPlayerId); + + expect( + events.where( + (VideoEvent e) => e.eventType == VideoEventType.playingUpdate), + equals([ + VideoEvent( + eventType: VideoEventType.playingUpdate, + isPlaying: true, + ) + ])); + }); + testWidgets('video playback lifecycle', (WidgetTester tester) async { final int videoPlayerId = await textureId; final Stream eventStream = From 23dd87ca223f8ea2e6439cdef7fd878578e28c28 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Wed, 22 Feb 2023 11:18:52 +0100 Subject: [PATCH 10/11] rename playingUpdate to isPlayingStateUpdate and update grammar --- packages/video_player/video_player/CHANGELOG.md | 2 +- .../video_player/video_player/lib/video_player.dart | 2 +- .../video_player/video_player_android/CHANGELOG.md | 2 +- .../io/flutter/plugins/videoplayer/VideoPlayer.java | 2 +- .../example/lib/mini_controller.dart | 2 +- .../lib/src/android_video_player.dart | 4 ++-- .../test/android_video_player_test.dart | 8 ++++---- .../video_player_avfoundation/CHANGELOG.md | 2 +- .../example/lib/mini_controller.dart | 2 +- .../ios/Classes/FLTVideoPlayerPlugin.m | 12 +++++++++--- .../lib/src/avfoundation_video_player.dart | 4 ++-- .../test/avfoundation_video_player_test.dart | 8 ++++---- .../video_player_platform_interface/CHANGELOG.md | 4 ++-- .../lib/video_player_platform_interface.dart | 12 ++++++++---- packages/video_player/video_player_web/CHANGELOG.md | 2 +- .../integration_test/video_player_web_test.dart | 13 +++++++------ .../video_player_web/lib/src/video_player.dart | 4 ++-- 17 files changed, 48 insertions(+), 37 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index ef329099fb90..15e589e6e37d 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.5.2 -* Synchronize `VideoPlayerValue.isPlaying` with underlying video player. +* Synchronizes `VideoPlayerValue.isPlaying` with underlying video player. * Updates minimum Flutter version to 3.0. ## 2.5.1 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 11226a1b5743..7fc65e41d9d6 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -399,7 +399,7 @@ class VideoPlayerController extends ValueNotifier { case VideoEventType.bufferingEnd: value = value.copyWith(isBuffering: false); break; - case VideoEventType.playingUpdate: + case VideoEventType.isPlayingStateUpdate: // TODO(maRci002): make VideoPlayerValue immutable and provide equals / hash if (event.isPlaying! != value.isPlaying) { value = value.copyWith(isPlaying: event.isPlaying); diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index d60c1ccb2946..85968f84fb3f 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.3.11 -* Synchronize `VideoPlayerValue.isPlaying` with `ExoPlayer`. +* Synchronizes `VideoPlayerValue.isPlaying` with `ExoPlayer`. * Updates minimum Flutter version to 3.0. ## 2.3.10 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 1e8c0a2dfa54..e1bf9ce3c903 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 @@ -237,7 +237,7 @@ public void onPlayerError(final PlaybackException error) { public void onIsPlayingChanged(boolean isPlaying) { if (eventSink != null) { Map event = new HashMap<>(); - event.put("event", "playingUpdate"); + event.put("event", "isPlayingStateUpdate"); event.put("isPlaying", isPlaying); eventSink.success(event); } diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index b1a47a6c3d5b..2c2ac6eb79c1 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -243,7 +243,7 @@ class MiniController extends ValueNotifier { case VideoEventType.bufferingEnd: value = value.copyWith(isBuffering: false); break; - case VideoEventType.playingUpdate: + case VideoEventType.isPlayingStateUpdate: // TODO(maRci002): make VideoPlayerValue immutable and provide equals / hash if (event.isPlaying! != value.isPlaying) { value = value.copyWith(isPlaying: event.isPlaying); 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 6ceaee484827..18af9da30d52 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 @@ -147,9 +147,9 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { return VideoEvent(eventType: VideoEventType.bufferingStart); case 'bufferingEnd': return VideoEvent(eventType: VideoEventType.bufferingEnd); - case 'playingUpdate': + case 'isPlayingStateUpdate': return VideoEvent( - eventType: VideoEventType.playingUpdate, + eventType: VideoEventType.isPlayingStateUpdate, isPlaying: map['isPlaying'] as bool, ); default: 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 66fa31c921cf..06fa7d3e8f4e 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 @@ -320,7 +320,7 @@ void main() { mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ - 'event': 'playingUpdate', + 'event': 'isPlayingStateUpdate', 'isPlaying': true, }), (ByteData? data) {}); @@ -331,7 +331,7 @@ void main() { mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ - 'event': 'playingUpdate', + 'event': 'isPlayingStateUpdate', 'isPlaying': false, }), (ByteData? data) {}); @@ -375,11 +375,11 @@ void main() { VideoEvent(eventType: VideoEventType.bufferingStart), VideoEvent(eventType: VideoEventType.bufferingEnd), VideoEvent( - eventType: VideoEventType.playingUpdate, + eventType: VideoEventType.isPlayingStateUpdate, isPlaying: true, ), VideoEvent( - eventType: VideoEventType.playingUpdate, + eventType: VideoEventType.isPlayingStateUpdate, isPlaying: false, ), ])); diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index e74ca0442768..9ba92a1cf897 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.3.9 -* Synchronize `VideoPlayerValue.isPlaying` with `AVPlayer`. +* Synchronizes `VideoPlayerValue.isPlaying` with `AVPlayer`. * Updates minimum Flutter version to 3.0. ## 2.3.8 diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index b1a47a6c3d5b..2c2ac6eb79c1 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -243,7 +243,7 @@ class MiniController extends ValueNotifier { case VideoEventType.bufferingEnd: value = value.copyWith(isBuffering: false); break; - case VideoEventType.playingUpdate: + case VideoEventType.isPlayingStateUpdate: // TODO(maRci002): make VideoPlayerValue immutable and provide equals / hash if (event.isPlaying! != value.isPlaying) { value = value.copyWith(isPlaying: event.isPlaying); diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index bd421cc07953..a440e77663f1 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -70,7 +70,7 @@ - (instancetype)initWithAsset:(NSString *)asset frameUpdater:(FLTFrameUpdater *) return [self initWithURL:[NSURL fileURLWithPath:path] frameUpdater:frameUpdater httpHeaders:@{}]; } -- (void)addObservers:(AVPlayerItem *)item player:(AVPlayer *)player { +- (void)addObserversForItem:(AVPlayerItem *)item player:(AVPlayer *)player { [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew @@ -99,6 +99,9 @@ - (void)addObservers:(AVPlayerItem *)item player:(AVPlayer *)player { forKeyPath:@"playbackBufferFull" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:playbackBufferFullContext]; + + // Add observer to AVPlayer instead of AVPlayerItem since the AVPlayerItem does not have a "rate" + // property [player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew @@ -257,7 +260,7 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item [self createVideoOutputAndDisplayLink:frameUpdater]; - [self addObservers:item player:_player]; + [self addObserversForItem:item player:_player]; [asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ] completionHandler:assetCompletionHandler]; @@ -323,9 +326,12 @@ - (void)observeValueForKeyPath:(NSString *)path _eventSink(@{@"event" : @"bufferingEnd"}); } } else if (context == rateContext) { + // Important: Make sure to cast the object to AVPlayer when observing the rate property, + // as it is not available in AVPlayerItem. AVPlayer *player = (AVPlayer *)object; if (_eventSink != nil) { - _eventSink(@{@"event" : @"playingUpdate", @"isPlaying" : player.rate > 0 ? @YES : @NO}); + _eventSink( + @{@"event" : @"isPlayingStateUpdate", @"isPlaying" : player.rate > 0 ? @YES : @NO}); } } } diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index 1c86bac9d405..868c5986f35f 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -146,9 +146,9 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { return VideoEvent(eventType: VideoEventType.bufferingStart); case 'bufferingEnd': return VideoEvent(eventType: VideoEventType.bufferingEnd); - case 'playingUpdate': + case 'isPlayingStateUpdate': return VideoEvent( - eventType: VideoEventType.playingUpdate, + eventType: VideoEventType.isPlayingStateUpdate, isPlaying: map['isPlaying'] as bool, ); default: diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index f4084e1bef73..1d10eed2286a 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -306,7 +306,7 @@ void main() { mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ - 'event': 'playingUpdate', + 'event': 'isPlayingStateUpdate', 'isPlaying': true, }), (ByteData? data) {}); @@ -317,7 +317,7 @@ void main() { mockChannel, const StandardMethodCodec() .encodeSuccessEnvelope({ - 'event': 'playingUpdate', + 'event': 'isPlayingStateUpdate', 'isPlaying': false, }), (ByteData? data) {}); @@ -354,11 +354,11 @@ void main() { VideoEvent(eventType: VideoEventType.bufferingStart), VideoEvent(eventType: VideoEventType.bufferingEnd), VideoEvent( - eventType: VideoEventType.playingUpdate, + eventType: VideoEventType.isPlayingStateUpdate, isPlaying: true, ), VideoEvent( - eventType: VideoEventType.playingUpdate, + eventType: VideoEventType.isPlayingStateUpdate, isPlaying: false, ), ])); diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index fb84b7a61a9f..9fc20b112876 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,7 +1,7 @@ ## 6.0.2 -* Add the `VideoEventType.playingUpdate` event to track changes in play / pause status with the -underlying video player. +* Add the `VideoEventType.isPlayingStateUpdate` event to track changes in play / pause state with +the underlying video player. * Updates minimum Flutter version to 3.0. ## 6.0.1 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 eab4436775fc..11af62b88ded 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 @@ -240,7 +240,7 @@ class VideoEvent { /// Play state changed. /// - /// Only used if [eventType] is [VideoEventType.playingUpdate]. + /// Only used if [eventType] is [VideoEventType.isPlayingStateUpdate]. final bool? isPlaying; @override @@ -287,9 +287,13 @@ enum VideoEventType { /// The video stopped to buffer. bufferingEnd, - /// The video started to play (for instance by user or phone call ended) - /// or the video has been paused (for instance by user or phone call ended) - playingUpdate, + /// Represents an event type emitted by the platform implementation when the + /// video playback state is updated, for example, when the video starts or + /// pauses due to user actions or phone calls, or other app media such as + /// music players. + /// Note that when [VideoPlayerOptions.mixWithOthers] is true, the video may + /// continue playing even during a phone call. + isPlayingStateUpdate, /// An unknown event has been received. unknown, diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 1ea7efbe94bb..5a65d2f773cc 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.0.14 -* Synchronize `VideoPlayerValue.isPlaying` with `VideoElement`. +* Synchronizes `VideoPlayerValue.isPlaying` with `VideoElement`. * Updates minimum Flutter version to 3.0. ## 2.0.13 diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart index 6750da6ee69b..1d70104aca91 100644 --- a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart @@ -169,7 +169,8 @@ void main() { expect(VideoPlayerPlatform.instance.setMixWithOthers(false), completes); }); - testWidgets('double call to play emit singe playingUpdate', + testWidgets( + 'double call to play will emit a single isPlayingStateUpdate event', (WidgetTester tester) async { final int videoPlayerId = await textureId; final Stream eventStream = @@ -192,11 +193,11 @@ void main() { await VideoPlayerPlatform.instance.pause(videoPlayerId); expect( - events.where( - (VideoEvent e) => e.eventType == VideoEventType.playingUpdate), + events.where((VideoEvent e) => + e.eventType == VideoEventType.isPlayingStateUpdate), equals([ VideoEvent( - eventType: VideoEventType.playingUpdate, + eventType: VideoEventType.isPlayingStateUpdate, isPlaying: true, ) ])); @@ -223,7 +224,7 @@ void main() { await VideoPlayerPlatform.instance.pause(videoPlayerId); // The expected list of event types should look like this: - // 1. playingUpdate (videoElement.onPlaying) + // 1. isPlayingStateUpdate (videoElement.onPlaying) // 2. bufferingStart, // 3. bufferingUpdate (videoElement.onWaiting), // 4. initialized (videoElement.onCanPlay), @@ -231,7 +232,7 @@ void main() { expect( events.map((VideoEvent e) => e.eventType), equals([ - VideoEventType.playingUpdate, + VideoEventType.isPlayingStateUpdate, VideoEventType.bufferingStart, VideoEventType.bufferingUpdate, VideoEventType.initialized, diff --git a/packages/video_player/video_player_web/lib/src/video_player.dart b/packages/video_player/video_player_web/lib/src/video_player.dart index 3c96a4d05467..bc0021dee34b 100644 --- a/packages/video_player/video_player_web/lib/src/video_player.dart +++ b/packages/video_player/video_player_web/lib/src/video_player.dart @@ -104,14 +104,14 @@ class VideoPlayer { _videoElement.onPlay.listen((dynamic _) { _eventController.add(VideoEvent( - eventType: VideoEventType.playingUpdate, + eventType: VideoEventType.isPlayingStateUpdate, isPlaying: true, )); }); _videoElement.onPause.listen((dynamic _) { _eventController.add(VideoEvent( - eventType: VideoEventType.playingUpdate, + eventType: VideoEventType.isPlayingStateUpdate, isPlaying: false, )); }); From c863ea6ae3c40a27b6a2600ff9f3081191877dca Mon Sep 17 00:00:00 2001 From: maRci002 Date: Wed, 22 Feb 2023 11:47:51 +0100 Subject: [PATCH 11/11] Make VideoPlayerValue, Caption immutable --- .../lib/src/closed_caption_file.dart | 21 ++++++- .../video_player/lib/video_player.dart | 58 +++++++++++++++---- .../video_player/test/video_player_test.dart | 47 +++++++-------- .../example/lib/mini_controller.dart | 47 +++++++++++---- .../example/lib/mini_controller.dart | 47 +++++++++++---- 5 files changed, 165 insertions(+), 55 deletions(-) diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart index 324ffc471ffe..91b44687d7d9 100644 --- a/packages/video_player/video_player/lib/src/closed_caption_file.dart +++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/foundation.dart' show objectRuntimeType; +import 'package:flutter/foundation.dart' show immutable, objectRuntimeType; import 'sub_rip.dart'; import 'web_vtt.dart'; @@ -32,6 +32,7 @@ abstract class ClosedCaptionFile { /// /// A typical closed captioning file will include several [Caption]s, each /// linked to a start and end time. +@immutable class Caption { /// Creates a new [Caption] object. /// @@ -74,4 +75,22 @@ class Caption { 'end: $end, ' 'text: $text)'; } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Caption && + runtimeType == other.runtimeType && + number == other.number && + start == other.start && + end == other.end && + text == other.text; + + @override + int get hashCode => Object.hash( + number, + start, + end, + text, + ); } diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 7fc65e41d9d6..03130a79caa3 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -33,10 +33,11 @@ VideoPlayerPlatform get _videoPlayerPlatform { /// The duration, current position, buffering state, error state and settings /// of a [VideoPlayerController]. +@immutable class VideoPlayerValue { /// Constructs a video with the given values. Only [duration] is required. The /// rest will initialize with default values when unset. - VideoPlayerValue({ + const VideoPlayerValue({ required this.duration, this.size = Size.zero, this.position = Duration.zero, @@ -54,11 +55,11 @@ class VideoPlayerValue { }); /// Returns an instance for a video that hasn't been loaded. - VideoPlayerValue.uninitialized() + const VideoPlayerValue.uninitialized() : this(duration: Duration.zero, isInitialized: false); /// Returns an instance with the given [errorDescription]. - VideoPlayerValue.erroneous(String errorDescription) + const VideoPlayerValue.erroneous(String errorDescription) : this( duration: Duration.zero, isInitialized: false, @@ -195,6 +196,44 @@ class VideoPlayerValue { 'playbackSpeed: $playbackSpeed, ' 'errorDescription: $errorDescription)'; } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is VideoPlayerValue && + runtimeType == other.runtimeType && + duration == other.duration && + position == other.position && + caption == other.caption && + captionOffset == other.captionOffset && + listEquals(buffered, other.buffered) && + isPlaying == other.isPlaying && + isLooping == other.isLooping && + isBuffering == other.isBuffering && + volume == other.volume && + playbackSpeed == other.playbackSpeed && + errorDescription == other.errorDescription && + size == other.size && + rotationCorrection == other.rotationCorrection && + isInitialized == other.isInitialized; + + @override + int get hashCode => Object.hash( + duration, + position, + caption, + captionOffset, + buffered, + isPlaying, + isLooping, + isBuffering, + volume, + playbackSpeed, + errorDescription, + size, + rotationCorrection, + isInitialized, + ); } /// Controls a platform video player, and provides updates when the state is @@ -221,7 +260,7 @@ class VideoPlayerController extends ValueNotifier { dataSourceType = DataSourceType.asset, formatHint = null, httpHeaders = const {}, - super(VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [VideoPlayerController] playing a video from obtained from /// the network. @@ -241,7 +280,7 @@ class VideoPlayerController extends ValueNotifier { }) : _closedCaptionFileFuture = closedCaptionFile, dataSourceType = DataSourceType.network, package = null, - super(VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [VideoPlayerController] playing a video from a file. /// @@ -254,7 +293,7 @@ class VideoPlayerController extends ValueNotifier { package = null, formatHint = null, httpHeaders = const {}, - super(VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [VideoPlayerController] playing a video from a contentUri. /// @@ -270,7 +309,7 @@ class VideoPlayerController extends ValueNotifier { package = null, formatHint = null, httpHeaders = const {}, - super(VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)); /// The URI to the video file. This will be in different formats depending on /// the [DataSourceType] of the original video. @@ -400,10 +439,7 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith(isBuffering: false); break; case VideoEventType.isPlayingStateUpdate: - // TODO(maRci002): make VideoPlayerValue immutable and provide equals / hash - if (event.isPlaying! != value.isPlaying) { - value = value.copyWith(isPlaying: event.isPlaying); - } + value = value.copyWith(isPlaying: event.isPlaying); break; case VideoEventType.unknown: break; 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 663fc9f8e897..f59c9feeca39 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -16,7 +16,7 @@ import 'package:video_player_platform_interface/video_player_platform_interface. class FakeController extends ValueNotifier implements VideoPlayerController { - FakeController() : super(VideoPlayerValue(duration: Duration.zero)); + FakeController() : super(const VideoPlayerValue(duration: Duration.zero)); FakeController.value(VideoPlayerValue value) : super(value); @@ -164,7 +164,8 @@ void main() { testWidgets('non-zero rotationCorrection value is used', (WidgetTester tester) async { final FakeController controller = FakeController.value( - VideoPlayerValue(duration: Duration.zero, rotationCorrection: 180)); + const VideoPlayerValue( + duration: Duration.zero, rotationCorrection: 180)); controller.textureId = 1; await tester.pumpWidget(VideoPlayer(controller)); final Transform actualRotationCorrection = @@ -184,7 +185,7 @@ void main() { testWidgets('no transform when rotationCorrection is zero', (WidgetTester tester) async { final FakeController controller = - FakeController.value(VideoPlayerValue(duration: Duration.zero)); + FakeController.value(const VideoPlayerValue(duration: Duration.zero)); controller.textureId = 1; await tester.pumpWidget(VideoPlayer(controller)); expect(find.byType(Transform), findsNothing); @@ -849,7 +850,7 @@ void main() { group('VideoPlayerValue', () { test('uninitialized()', () { - final VideoPlayerValue uninitialized = VideoPlayerValue.uninitialized(); + const VideoPlayerValue uninitialized = VideoPlayerValue.uninitialized(); expect(uninitialized.duration, equals(Duration.zero)); expect(uninitialized.position, equals(Duration.zero)); @@ -870,7 +871,7 @@ void main() { test('erroneous()', () { const String errorMessage = 'foo'; - final VideoPlayerValue error = VideoPlayerValue.erroneous(errorMessage); + const VideoPlayerValue error = VideoPlayerValue.erroneous(errorMessage); expect(error.duration, equals(Duration.zero)); expect(error.position, equals(Duration.zero)); @@ -940,26 +941,26 @@ void main() { group('copyWith()', () { test('exact copy', () { - final VideoPlayerValue original = VideoPlayerValue.uninitialized(); + const VideoPlayerValue original = VideoPlayerValue.uninitialized(); final VideoPlayerValue exactCopy = original.copyWith(); expect(exactCopy.toString(), original.toString()); }); test('errorDescription is not persisted when copy with null', () { - final VideoPlayerValue original = VideoPlayerValue.erroneous('error'); + const VideoPlayerValue original = VideoPlayerValue.erroneous('error'); final VideoPlayerValue copy = original.copyWith(errorDescription: null); expect(copy.errorDescription, null); }); test('errorDescription is changed when copy with another error', () { - final VideoPlayerValue original = VideoPlayerValue.erroneous('error'); + const VideoPlayerValue original = VideoPlayerValue.erroneous('error'); final VideoPlayerValue copy = original.copyWith(errorDescription: 'new error'); expect(copy.errorDescription, 'new error'); }); test('errorDescription is changed when copy with error', () { - final VideoPlayerValue original = VideoPlayerValue.uninitialized(); + const VideoPlayerValue original = VideoPlayerValue.uninitialized(); final VideoPlayerValue copy = original.copyWith(errorDescription: 'new error'); @@ -969,45 +970,45 @@ void main() { group('aspectRatio', () { test('640x480 -> 4:3', () { - final VideoPlayerValue value = VideoPlayerValue( + const VideoPlayerValue value = VideoPlayerValue( isInitialized: true, - size: const Size(640, 480), - duration: const Duration(seconds: 1), + size: Size(640, 480), + duration: Duration(seconds: 1), ); expect(value.aspectRatio, 4 / 3); }); test('no size -> 1.0', () { - final VideoPlayerValue value = VideoPlayerValue( + const VideoPlayerValue value = VideoPlayerValue( isInitialized: true, - duration: const Duration(seconds: 1), + duration: Duration(seconds: 1), ); expect(value.aspectRatio, 1.0); }); test('height = 0 -> 1.0', () { - final VideoPlayerValue value = VideoPlayerValue( + const VideoPlayerValue value = VideoPlayerValue( isInitialized: true, - size: const Size(640, 0), - duration: const Duration(seconds: 1), + size: Size(640, 0), + duration: Duration(seconds: 1), ); expect(value.aspectRatio, 1.0); }); test('width = 0 -> 1.0', () { - final VideoPlayerValue value = VideoPlayerValue( + const VideoPlayerValue value = VideoPlayerValue( isInitialized: true, - size: const Size(0, 480), - duration: const Duration(seconds: 1), + size: Size(0, 480), + duration: Duration(seconds: 1), ); expect(value.aspectRatio, 1.0); }); test('negative aspect ratio -> 1.0', () { - final VideoPlayerValue value = VideoPlayerValue( + const VideoPlayerValue value = VideoPlayerValue( isInitialized: true, - size: const Size(640, -480), - duration: const Duration(seconds: 1), + size: Size(640, -480), + duration: Duration(seconds: 1), ); expect(value.aspectRatio, 1.0); }); diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 2c2ac6eb79c1..ad28b17a168c 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; @@ -24,10 +25,11 @@ VideoPlayerPlatform get _platform { /// The duration, current position, buffering state, error state and settings /// of a [MiniController]. +@immutable class VideoPlayerValue { /// Constructs a video with the given values. Only [duration] is required. The /// rest will initialize with default values when unset. - VideoPlayerValue({ + const VideoPlayerValue({ required this.duration, this.size = Size.zero, this.position = Duration.zero, @@ -40,11 +42,11 @@ class VideoPlayerValue { }); /// Returns an instance for a video that hasn't been loaded. - VideoPlayerValue.uninitialized() + const VideoPlayerValue.uninitialized() : this(duration: Duration.zero, isInitialized: false); /// Returns an instance with the given [errorDescription]. - VideoPlayerValue.erroneous(String errorDescription) + const VideoPlayerValue.erroneous(String errorDescription) : this( duration: Duration.zero, isInitialized: false, @@ -127,6 +129,34 @@ class VideoPlayerValue { errorDescription: errorDescription ?? this.errorDescription, ); } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is VideoPlayerValue && + runtimeType == other.runtimeType && + duration == other.duration && + position == other.position && + listEquals(buffered, other.buffered) && + isPlaying == other.isPlaying && + isBuffering == other.isBuffering && + playbackSpeed == other.playbackSpeed && + errorDescription == other.errorDescription && + size == other.size && + isInitialized == other.isInitialized; + + @override + int get hashCode => Object.hash( + duration, + position, + buffered, + isPlaying, + isBuffering, + playbackSpeed, + errorDescription, + size, + isInitialized, + ); } /// A very minimal version of `VideoPlayerController` for running the example @@ -139,21 +169,21 @@ class MiniController extends ValueNotifier { /// package and null otherwise. MiniController.asset(this.dataSource, {this.package}) : dataSourceType = DataSourceType.asset, - super(VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from /// the network. MiniController.network(this.dataSource) : dataSourceType = DataSourceType.network, package = null, - super(VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from a file. MiniController.file(File file) : dataSource = Uri.file(file.absolute.path).toString(), dataSourceType = DataSourceType.file, package = null, - super(VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)); /// The URI to the video file. This will be in different formats depending on /// the [DataSourceType] of the original video. @@ -244,10 +274,7 @@ class MiniController extends ValueNotifier { value = value.copyWith(isBuffering: false); break; case VideoEventType.isPlayingStateUpdate: - // TODO(maRci002): make VideoPlayerValue immutable and provide equals / hash - if (event.isPlaying! != value.isPlaying) { - value = value.copyWith(isPlaying: event.isPlaying); - } + value = value.copyWith(isPlaying: event.isPlaying); break; case VideoEventType.unknown: break; diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 2c2ac6eb79c1..ad28b17a168c 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; @@ -24,10 +25,11 @@ VideoPlayerPlatform get _platform { /// The duration, current position, buffering state, error state and settings /// of a [MiniController]. +@immutable class VideoPlayerValue { /// Constructs a video with the given values. Only [duration] is required. The /// rest will initialize with default values when unset. - VideoPlayerValue({ + const VideoPlayerValue({ required this.duration, this.size = Size.zero, this.position = Duration.zero, @@ -40,11 +42,11 @@ class VideoPlayerValue { }); /// Returns an instance for a video that hasn't been loaded. - VideoPlayerValue.uninitialized() + const VideoPlayerValue.uninitialized() : this(duration: Duration.zero, isInitialized: false); /// Returns an instance with the given [errorDescription]. - VideoPlayerValue.erroneous(String errorDescription) + const VideoPlayerValue.erroneous(String errorDescription) : this( duration: Duration.zero, isInitialized: false, @@ -127,6 +129,34 @@ class VideoPlayerValue { errorDescription: errorDescription ?? this.errorDescription, ); } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is VideoPlayerValue && + runtimeType == other.runtimeType && + duration == other.duration && + position == other.position && + listEquals(buffered, other.buffered) && + isPlaying == other.isPlaying && + isBuffering == other.isBuffering && + playbackSpeed == other.playbackSpeed && + errorDescription == other.errorDescription && + size == other.size && + isInitialized == other.isInitialized; + + @override + int get hashCode => Object.hash( + duration, + position, + buffered, + isPlaying, + isBuffering, + playbackSpeed, + errorDescription, + size, + isInitialized, + ); } /// A very minimal version of `VideoPlayerController` for running the example @@ -139,21 +169,21 @@ class MiniController extends ValueNotifier { /// package and null otherwise. MiniController.asset(this.dataSource, {this.package}) : dataSourceType = DataSourceType.asset, - super(VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from /// the network. MiniController.network(this.dataSource) : dataSourceType = DataSourceType.network, package = null, - super(VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from a file. MiniController.file(File file) : dataSource = Uri.file(file.absolute.path).toString(), dataSourceType = DataSourceType.file, package = null, - super(VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)); /// The URI to the video file. This will be in different formats depending on /// the [DataSourceType] of the original video. @@ -244,10 +274,7 @@ class MiniController extends ValueNotifier { value = value.copyWith(isBuffering: false); break; case VideoEventType.isPlayingStateUpdate: - // TODO(maRci002): make VideoPlayerValue immutable and provide equals / hash - if (event.isPlaying! != value.isPlaying) { - value = value.copyWith(isPlaying: event.isPlaying); - } + value = value.copyWith(isPlaying: event.isPlaying); break; case VideoEventType.unknown: break;