From 7837ddbaa8d57cc6be6f8ad4ecfc2b19c6bbeb74 Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 14:51:44 +0530 Subject: [PATCH 01/22] Added setPlayBackSpeed method where provided speed at which video should be played is set to playbackparameter instance. The same instance is been set to exoplayer. --- .../plugins/videoplayer/VideoPlayerPlugin.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index de4f44cdb567..5aa8c37f7f09 100644 --- a/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -16,6 +16,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player.DefaultEventListener; import com.google.android.exoplayer2.SimpleExoPlayer; @@ -218,6 +219,14 @@ long getPosition() { return exoPlayer.getCurrentPosition(); } + void setPlayBackSpeed(double value) { + float bracketedValue = (float) value; + PlaybackParameters existingParam = exoPlayer.getPlaybackParameters(); + PlaybackParameters newParameter = + new PlaybackParameters(bracketedValue, existingParam.pitch, existingParam.skipSilence); + exoPlayer.setPlaybackParameters(newParameter); + } + private void sendInitialized() { if (isInitialized) { Map event = new HashMap<>(); @@ -390,6 +399,10 @@ private void onMethodCall(MethodCall call, Result result, long textureId, VideoP videoPlayers.remove(textureId); result.success(null); break; + case "setPlayBackSpeed": + player.setPlayBackSpeed((Double) call.argument("speed")); + result.success(null); + break; default: result.notImplemented(); break; From 8578587997e8154483e51a9aac9a688289a495bf Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 14:58:12 +0530 Subject: [PATCH 02/22] Added setSpeed method which pass the value to videoplayer plugin Value can be 1x, 2x and accordingly speed of video will be set. To watch video in slow motion set value of speed less than 1.0 Similary set value more than 1.0 to watch the playback in high speed. By Default speed of video will be 1.0 which is normal speed. --- packages/video_player/lib/video_player.dart | 31 ++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/video_player/lib/video_player.dart b/packages/video_player/lib/video_player.dart index 441057de32b6..afcc1ebf47de 100644 --- a/packages/video_player/lib/video_player.dart +++ b/packages/video_player/lib/video_player.dart @@ -48,6 +48,7 @@ class VideoPlayerValue { this.isBuffering = false, this.volume = 1.0, this.errorDescription, + this.speed = 1.0, }); VideoPlayerValue.uninitialized() : this(duration: null); @@ -88,6 +89,9 @@ class VideoPlayerValue { /// Is null when [initialized] is false. final Size size; + ///The Current speed of the playback. + final double speed; + bool get initialized => duration != null; bool get hasError => errorDescription != null; double get aspectRatio => size != null ? size.width / size.height : 1.0; @@ -102,6 +106,7 @@ class VideoPlayerValue { bool isBuffering, double volume, String errorDescription, + double speed, }) { return VideoPlayerValue( duration: duration ?? this.duration, @@ -112,6 +117,7 @@ class VideoPlayerValue { isLooping: isLooping ?? this.isLooping, isBuffering: isBuffering ?? this.isBuffering, volume: volume ?? this.volume, + speed: speed ?? this.speed, errorDescription: errorDescription ?? this.errorDescription, ); } @@ -127,7 +133,8 @@ class VideoPlayerValue { 'isLooping: $isLooping, ' 'isBuffering: $isBuffering' 'volume: $volume, ' - 'errorDescription: $errorDescription)'; + 'errorDescription: $errorDescription' + 'speed: $speed)'; } } @@ -240,6 +247,7 @@ class VideoPlayerController extends ValueNotifier { _applyLooping(); _applyVolume(); _applyPlayPause(); + _applyPlayBackSpeed(); break; case 'completed': value = value.copyWith(isPlaying: false); @@ -419,6 +427,27 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith(volume: volume.clamp(0.0, 1.0)); await _applyVolume(); } + + Future _applyPlayBackSpeed() async { + if (!value.initialized || _isDisposed) { + return; + } + + // ignore: strong_mode_implicit_dynamic_method + await _channel.invokeMethod( + 'setPlayBackSpeed', + {'textureId': _textureId, 'speed': value.speed}, + ); + } + + /// Sets the playback speed of [this]. + /// + /// [speed] can be 0.5x, 1x, 2x + /// by default speed value is 1.0 + Future setSpeed(double speed) async { + value = value.copyWith(speed: speed); + await _applyPlayBackSpeed(); + } } class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { From 30e30ab9d828fece18e0d7137d9a8b1482059a76 Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 15:00:45 +0530 Subject: [PATCH 03/22] Test cases for normal speed as well as for changed speed has been passed --- .../video_player/test/video_player_test.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/video_player/test/video_player_test.dart b/packages/video_player/test/video_player_test.dart index 94bebbd331d8..3911b97bf283 100644 --- a/packages/video_player/test/video_player_test.dart +++ b/packages/video_player/test/video_player_test.dart @@ -37,6 +37,10 @@ class FakeController extends ValueNotifier Future play() async {} @override Future setLooping(bool looping) async {} + @override + Future setSpeed(double speed) async { + value = value.copyWith(speed: speed); + } } void main() { @@ -73,4 +77,19 @@ void main() { ), findsOneWidget); }); + + testWidgets('default playback speed', (WidgetTester tester) async { + final FakeController controller = FakeController(); + controller.textureId = 101; + await tester.pumpWidget(VideoPlayer(controller)); + expect(controller.value.speed, 1.0); + }); + + testWidgets('Changed playback speed', (WidgetTester tester) async { + final FakeController controller = FakeController(); + controller.textureId = 101; + controller.setSpeed(1.5); + await tester.pumpWidget(VideoPlayer(controller)); + expect(controller.value.speed, 1.5); + }); } From d65e7dc4302764b63b29732eb10c1db17840eaf1 Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 15:05:13 +0530 Subject: [PATCH 04/22] CHANGEDLOG.md has been updated with appropriate message. --- packages/video_player/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/video_player/CHANGELOG.md b/packages/video_player/CHANGELOG.md index de5f66e5afd7..7570c1b40aa1 100644 --- a/packages/video_player/CHANGELOG.md +++ b/packages/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.0+5 + +* Implemented playback speed feature. + ## 0.10.0+4 * Android: Upgrade ExoPlayer to 2.9.6. From f8d3491cd394ee4d42e73ca52680501aed023574 Mon Sep 17 00:00:00 2001 From: Rodrigo Castro Date: Tue, 23 Apr 2019 23:04:58 +0100 Subject: [PATCH 05/22] - Implemented playback speed feature on iOS --- .../video_player/ios/Classes/VideoPlayerPlugin.m | 16 ++++++++++++++++ packages/video_player/lib/video_player.dart | 12 +++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/video_player/ios/Classes/VideoPlayerPlugin.m b/packages/video_player/ios/Classes/VideoPlayerPlugin.m index 5554b21006a4..32a75c8ed4ba 100644 --- a/packages/video_player/ios/Classes/VideoPlayerPlugin.m +++ b/packages/video_player/ios/Classes/VideoPlayerPlugin.m @@ -342,6 +342,19 @@ - (void)setVolume:(double)volume { _player.volume = (volume < 0.0) ? 0.0 : ((volume > 1.0) ? 1.0 : volume); } +- (void)setPlayBackSpeed:(double)speed { + if (speed == 1.0 || speed == 0.0) { + _player.rate = speed; + } else if (speed < 0 || speed > 2.0) { + NSLog(@"Speed outside supported range %f", speed); + } else if ((speed > 1.0 && _player.currentItem.canPlayFastForward) || + (speed < 1.0 && _player.currentItem.canPlaySlowForward)) { + _player.rate = speed; + } else { + NSLog(@"Unsupported speed. Cannot play fast/slow forward: %f", speed); + } +} + - (CVPixelBufferRef)copyPixelBuffer { CMTime outputItemTime = [_videoOutput itemTimeForHostTime:CACurrentMediaTime()]; if ([_videoOutput hasNewPixelBufferForItemTime:outputItemTime]) { @@ -494,6 +507,9 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([@"pause" isEqualToString:call.method]) { [player pause]; result(nil); + } else if ([@"setPlayBackSpeed" isEqualToString:call.method]) { + [player setPlayBackSpeed:[[argsMap objectForKey:@"speed"] doubleValue]]; + result(nil); } else { result(FlutterMethodNotImplemented); } diff --git a/packages/video_player/lib/video_player.dart b/packages/video_player/lib/video_player.dart index 99302dcc0db4..661ddf2df0af 100644 --- a/packages/video_player/lib/video_player.dart +++ b/packages/video_player/lib/video_player.dart @@ -93,7 +93,9 @@ class VideoPlayerValue { final double speed; bool get initialized => duration != null; + bool get hasError => errorDescription != null; + double get aspectRatio => size != null ? size.width / size.height : 1.0; VideoPlayerValue copyWith({ @@ -247,7 +249,6 @@ class VideoPlayerController extends ValueNotifier { _applyLooping(); _applyVolume(); _applyPlayPause(); - _applyPlayBackSpeed(); break; case 'completed': value = value.copyWith(isPlaying: false, position: value.duration); @@ -359,6 +360,9 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith(position: newPosition); }, ); + + // Ensure the video is played at the correct speed + await _applyPlayBackSpeed(); } else { _timer?.cancel(); // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. @@ -433,6 +437,12 @@ class VideoPlayerController extends ValueNotifier { return; } + // On iOS setting the speed will start playing the video automatically + // Do not change the video speed until after the video is played + if (!value.isPlaying) { + return; + } + // ignore: strong_mode_implicit_dynamic_method await _channel.invokeMethod( 'setPlayBackSpeed', From 87dc34d11a31444887caa56e925d8852e980ec0e Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 14:51:44 +0530 Subject: [PATCH 06/22] Added setPlayBackSpeed method where provided speed at which video should be played is set to playbackparameter instance. The same instance is been set to exoplayer. --- .../plugins/videoplayer/VideoPlayerPlugin.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index 0fdaa2572c92..70535ff953a1 100644 --- a/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -16,6 +16,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player.DefaultEventListener; import com.google.android.exoplayer2.SimpleExoPlayer; @@ -224,6 +225,14 @@ long getPosition() { return exoPlayer.getCurrentPosition(); } + void setPlayBackSpeed(double value) { + float bracketedValue = (float) value; + PlaybackParameters existingParam = exoPlayer.getPlaybackParameters(); + PlaybackParameters newParameter = + new PlaybackParameters(bracketedValue, existingParam.pitch, existingParam.skipSilence); + exoPlayer.setPlaybackParameters(newParameter); + } + private void sendInitialized() { if (isInitialized) { Map event = new HashMap<>(); @@ -396,6 +405,10 @@ private void onMethodCall(MethodCall call, Result result, long textureId, VideoP videoPlayers.remove(textureId); result.success(null); break; + case "setPlayBackSpeed": + player.setPlayBackSpeed((Double) call.argument("speed")); + result.success(null); + break; default: result.notImplemented(); break; From 9138402e03b39791f3e9be835ceee9de4f46f34a Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 14:58:12 +0530 Subject: [PATCH 07/22] Added setSpeed method which pass the value to videoplayer plugin Value can be 1x, 2x and accordingly speed of video will be set. To watch video in slow motion set value of speed less than 1.0 Similary set value more than 1.0 to watch the playback in high speed. By Default speed of video will be 1.0 which is normal speed. --- packages/video_player/lib/video_player.dart | 31 ++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/video_player/lib/video_player.dart b/packages/video_player/lib/video_player.dart index ce4ac70cff43..99302dcc0db4 100644 --- a/packages/video_player/lib/video_player.dart +++ b/packages/video_player/lib/video_player.dart @@ -48,6 +48,7 @@ class VideoPlayerValue { this.isBuffering = false, this.volume = 1.0, this.errorDescription, + this.speed = 1.0, }); VideoPlayerValue.uninitialized() : this(duration: null); @@ -88,6 +89,9 @@ class VideoPlayerValue { /// Is null when [initialized] is false. final Size size; + ///The Current speed of the playback. + final double speed; + bool get initialized => duration != null; bool get hasError => errorDescription != null; double get aspectRatio => size != null ? size.width / size.height : 1.0; @@ -102,6 +106,7 @@ class VideoPlayerValue { bool isBuffering, double volume, String errorDescription, + double speed, }) { return VideoPlayerValue( duration: duration ?? this.duration, @@ -112,6 +117,7 @@ class VideoPlayerValue { isLooping: isLooping ?? this.isLooping, isBuffering: isBuffering ?? this.isBuffering, volume: volume ?? this.volume, + speed: speed ?? this.speed, errorDescription: errorDescription ?? this.errorDescription, ); } @@ -127,7 +133,8 @@ class VideoPlayerValue { 'isLooping: $isLooping, ' 'isBuffering: $isBuffering' 'volume: $volume, ' - 'errorDescription: $errorDescription)'; + 'errorDescription: $errorDescription' + 'speed: $speed)'; } } @@ -240,6 +247,7 @@ class VideoPlayerController extends ValueNotifier { _applyLooping(); _applyVolume(); _applyPlayPause(); + _applyPlayBackSpeed(); break; case 'completed': value = value.copyWith(isPlaying: false, position: value.duration); @@ -419,6 +427,27 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith(volume: volume.clamp(0.0, 1.0)); await _applyVolume(); } + + Future _applyPlayBackSpeed() async { + if (!value.initialized || _isDisposed) { + return; + } + + // ignore: strong_mode_implicit_dynamic_method + await _channel.invokeMethod( + 'setPlayBackSpeed', + {'textureId': _textureId, 'speed': value.speed}, + ); + } + + /// Sets the playback speed of [this]. + /// + /// [speed] can be 0.5x, 1x, 2x + /// by default speed value is 1.0 + Future setSpeed(double speed) async { + value = value.copyWith(speed: speed); + await _applyPlayBackSpeed(); + } } class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { From bca9603377aace17fd5a4cde077dac11d4769ce5 Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 15:00:45 +0530 Subject: [PATCH 08/22] Test cases for normal speed as well as for changed speed has been passed --- .../video_player/test/video_player_test.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/video_player/test/video_player_test.dart b/packages/video_player/test/video_player_test.dart index 94bebbd331d8..3911b97bf283 100644 --- a/packages/video_player/test/video_player_test.dart +++ b/packages/video_player/test/video_player_test.dart @@ -37,6 +37,10 @@ class FakeController extends ValueNotifier Future play() async {} @override Future setLooping(bool looping) async {} + @override + Future setSpeed(double speed) async { + value = value.copyWith(speed: speed); + } } void main() { @@ -73,4 +77,19 @@ void main() { ), findsOneWidget); }); + + testWidgets('default playback speed', (WidgetTester tester) async { + final FakeController controller = FakeController(); + controller.textureId = 101; + await tester.pumpWidget(VideoPlayer(controller)); + expect(controller.value.speed, 1.0); + }); + + testWidgets('Changed playback speed', (WidgetTester tester) async { + final FakeController controller = FakeController(); + controller.textureId = 101; + controller.setSpeed(1.5); + await tester.pumpWidget(VideoPlayer(controller)); + expect(controller.value.speed, 1.5); + }); } From 8d16064b5d3a2a347d0d961486b5c540b58b19a3 Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 15:05:13 +0530 Subject: [PATCH 09/22] CHANGEDLOG.md has been updated with appropriate message. --- packages/video_player/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/video_player/CHANGELOG.md b/packages/video_player/CHANGELOG.md index 362b6b02ea90..0aac2c78928b 100644 --- a/packages/video_player/CHANGELOG.md +++ b/packages/video_player/CHANGELOG.md @@ -2,7 +2,11 @@ * Android: Fix missing call to `event.put("event", "completed");` which makes it possible to detect when the video is over. -## 0.10.0+5 +## 0.10.0+5.1 + +* Implemented playback speed feature. + +## 0.10.0+5.2 * Fixed iOS build warnings about implicit retains. From da322f5c8eeb09107f4cabc3740b89ce651d1ca9 Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 14:51:44 +0530 Subject: [PATCH 10/22] Added setPlayBackSpeed method where provided speed at which video should be played is set to playbackparameter instance. The same instance is been set to exoplayer. --- .../plugins/videoplayer/VideoPlayerPlugin.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index 0fdaa2572c92..70535ff953a1 100644 --- a/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -16,6 +16,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player.DefaultEventListener; import com.google.android.exoplayer2.SimpleExoPlayer; @@ -224,6 +225,14 @@ long getPosition() { return exoPlayer.getCurrentPosition(); } + void setPlayBackSpeed(double value) { + float bracketedValue = (float) value; + PlaybackParameters existingParam = exoPlayer.getPlaybackParameters(); + PlaybackParameters newParameter = + new PlaybackParameters(bracketedValue, existingParam.pitch, existingParam.skipSilence); + exoPlayer.setPlaybackParameters(newParameter); + } + private void sendInitialized() { if (isInitialized) { Map event = new HashMap<>(); @@ -396,6 +405,10 @@ private void onMethodCall(MethodCall call, Result result, long textureId, VideoP videoPlayers.remove(textureId); result.success(null); break; + case "setPlayBackSpeed": + player.setPlayBackSpeed((Double) call.argument("speed")); + result.success(null); + break; default: result.notImplemented(); break; From aec50ac7121508feae3f88e5b0c3c119f0045f0e Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 14:58:12 +0530 Subject: [PATCH 11/22] Added setSpeed method which pass the value to videoplayer plugin Value can be 1x, 2x and accordingly speed of video will be set. To watch video in slow motion set value of speed less than 1.0 Similary set value more than 1.0 to watch the playback in high speed. By Default speed of video will be 1.0 which is normal speed. --- packages/video_player/lib/video_player.dart | 31 ++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/video_player/lib/video_player.dart b/packages/video_player/lib/video_player.dart index ce4ac70cff43..99302dcc0db4 100644 --- a/packages/video_player/lib/video_player.dart +++ b/packages/video_player/lib/video_player.dart @@ -48,6 +48,7 @@ class VideoPlayerValue { this.isBuffering = false, this.volume = 1.0, this.errorDescription, + this.speed = 1.0, }); VideoPlayerValue.uninitialized() : this(duration: null); @@ -88,6 +89,9 @@ class VideoPlayerValue { /// Is null when [initialized] is false. final Size size; + ///The Current speed of the playback. + final double speed; + bool get initialized => duration != null; bool get hasError => errorDescription != null; double get aspectRatio => size != null ? size.width / size.height : 1.0; @@ -102,6 +106,7 @@ class VideoPlayerValue { bool isBuffering, double volume, String errorDescription, + double speed, }) { return VideoPlayerValue( duration: duration ?? this.duration, @@ -112,6 +117,7 @@ class VideoPlayerValue { isLooping: isLooping ?? this.isLooping, isBuffering: isBuffering ?? this.isBuffering, volume: volume ?? this.volume, + speed: speed ?? this.speed, errorDescription: errorDescription ?? this.errorDescription, ); } @@ -127,7 +133,8 @@ class VideoPlayerValue { 'isLooping: $isLooping, ' 'isBuffering: $isBuffering' 'volume: $volume, ' - 'errorDescription: $errorDescription)'; + 'errorDescription: $errorDescription' + 'speed: $speed)'; } } @@ -240,6 +247,7 @@ class VideoPlayerController extends ValueNotifier { _applyLooping(); _applyVolume(); _applyPlayPause(); + _applyPlayBackSpeed(); break; case 'completed': value = value.copyWith(isPlaying: false, position: value.duration); @@ -419,6 +427,27 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith(volume: volume.clamp(0.0, 1.0)); await _applyVolume(); } + + Future _applyPlayBackSpeed() async { + if (!value.initialized || _isDisposed) { + return; + } + + // ignore: strong_mode_implicit_dynamic_method + await _channel.invokeMethod( + 'setPlayBackSpeed', + {'textureId': _textureId, 'speed': value.speed}, + ); + } + + /// Sets the playback speed of [this]. + /// + /// [speed] can be 0.5x, 1x, 2x + /// by default speed value is 1.0 + Future setSpeed(double speed) async { + value = value.copyWith(speed: speed); + await _applyPlayBackSpeed(); + } } class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { From 1ed0ead68d2e9796f3515f1c9318e9d23e7a851d Mon Sep 17 00:00:00 2001 From: John Nair Date: Tue, 26 Mar 2019 15:00:45 +0530 Subject: [PATCH 12/22] Test cases for normal speed as well as for changed speed has been passed --- .../video_player/test/video_player_test.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/video_player/test/video_player_test.dart b/packages/video_player/test/video_player_test.dart index 94bebbd331d8..3911b97bf283 100644 --- a/packages/video_player/test/video_player_test.dart +++ b/packages/video_player/test/video_player_test.dart @@ -37,6 +37,10 @@ class FakeController extends ValueNotifier Future play() async {} @override Future setLooping(bool looping) async {} + @override + Future setSpeed(double speed) async { + value = value.copyWith(speed: speed); + } } void main() { @@ -73,4 +77,19 @@ void main() { ), findsOneWidget); }); + + testWidgets('default playback speed', (WidgetTester tester) async { + final FakeController controller = FakeController(); + controller.textureId = 101; + await tester.pumpWidget(VideoPlayer(controller)); + expect(controller.value.speed, 1.0); + }); + + testWidgets('Changed playback speed', (WidgetTester tester) async { + final FakeController controller = FakeController(); + controller.textureId = 101; + controller.setSpeed(1.5); + await tester.pumpWidget(VideoPlayer(controller)); + expect(controller.value.speed, 1.5); + }); } From ea77b8da7e0e9372cff88fe24e31bf2ae70cdb50 Mon Sep 17 00:00:00 2001 From: Rodrigo Castro Date: Tue, 23 Apr 2019 23:04:58 +0100 Subject: [PATCH 13/22] - Implemented playback speed feature on iOS --- .../video_player/ios/Classes/VideoPlayerPlugin.m | 16 ++++++++++++++++ packages/video_player/lib/video_player.dart | 12 +++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/video_player/ios/Classes/VideoPlayerPlugin.m b/packages/video_player/ios/Classes/VideoPlayerPlugin.m index 5554b21006a4..32a75c8ed4ba 100644 --- a/packages/video_player/ios/Classes/VideoPlayerPlugin.m +++ b/packages/video_player/ios/Classes/VideoPlayerPlugin.m @@ -342,6 +342,19 @@ - (void)setVolume:(double)volume { _player.volume = (volume < 0.0) ? 0.0 : ((volume > 1.0) ? 1.0 : volume); } +- (void)setPlayBackSpeed:(double)speed { + if (speed == 1.0 || speed == 0.0) { + _player.rate = speed; + } else if (speed < 0 || speed > 2.0) { + NSLog(@"Speed outside supported range %f", speed); + } else if ((speed > 1.0 && _player.currentItem.canPlayFastForward) || + (speed < 1.0 && _player.currentItem.canPlaySlowForward)) { + _player.rate = speed; + } else { + NSLog(@"Unsupported speed. Cannot play fast/slow forward: %f", speed); + } +} + - (CVPixelBufferRef)copyPixelBuffer { CMTime outputItemTime = [_videoOutput itemTimeForHostTime:CACurrentMediaTime()]; if ([_videoOutput hasNewPixelBufferForItemTime:outputItemTime]) { @@ -494,6 +507,9 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([@"pause" isEqualToString:call.method]) { [player pause]; result(nil); + } else if ([@"setPlayBackSpeed" isEqualToString:call.method]) { + [player setPlayBackSpeed:[[argsMap objectForKey:@"speed"] doubleValue]]; + result(nil); } else { result(FlutterMethodNotImplemented); } diff --git a/packages/video_player/lib/video_player.dart b/packages/video_player/lib/video_player.dart index 99302dcc0db4..661ddf2df0af 100644 --- a/packages/video_player/lib/video_player.dart +++ b/packages/video_player/lib/video_player.dart @@ -93,7 +93,9 @@ class VideoPlayerValue { final double speed; bool get initialized => duration != null; + bool get hasError => errorDescription != null; + double get aspectRatio => size != null ? size.width / size.height : 1.0; VideoPlayerValue copyWith({ @@ -247,7 +249,6 @@ class VideoPlayerController extends ValueNotifier { _applyLooping(); _applyVolume(); _applyPlayPause(); - _applyPlayBackSpeed(); break; case 'completed': value = value.copyWith(isPlaying: false, position: value.duration); @@ -359,6 +360,9 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith(position: newPosition); }, ); + + // Ensure the video is played at the correct speed + await _applyPlayBackSpeed(); } else { _timer?.cancel(); // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. @@ -433,6 +437,12 @@ class VideoPlayerController extends ValueNotifier { return; } + // On iOS setting the speed will start playing the video automatically + // Do not change the video speed until after the video is played + if (!value.isPlaying) { + return; + } + // ignore: strong_mode_implicit_dynamic_method await _channel.invokeMethod( 'setPlayBackSpeed', From 5583c6b7d74c8b02e50b52c587219e8097c0719d Mon Sep 17 00:00:00 2001 From: John Nair Date: Mon, 27 May 2019 13:22:10 +0530 Subject: [PATCH 14/22] - setPlayBackSpeed renamed to setSpeed. --- .../io/flutter/plugins/videoplayer/VideoPlayerPlugin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index ef90675e2562..8c32af7d0edd 100644 --- a/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -233,7 +233,7 @@ long getPosition() { return exoPlayer.getCurrentPosition(); } - void setPlayBackSpeed(double value) { + void setSpeed(double value) { float bracketedValue = (float) value; PlaybackParameters existingParam = exoPlayer.getPlaybackParameters(); PlaybackParameters newParameter = @@ -411,8 +411,8 @@ private void onMethodCall(MethodCall call, Result result, long textureId, VideoP videoPlayers.remove(textureId); result.success(null); break; - case "setPlayBackSpeed": - player.setPlayBackSpeed((Double) call.argument("speed")); + case "setSpeed": + player.setSpeed((Double) call.argument("speed")); result.success(null); break; default: From 52d537c2162252fbe8aa9f8af5b236eb36eb37a0 Mon Sep 17 00:00:00 2001 From: John Nair Date: Mon, 27 May 2019 13:23:43 +0530 Subject: [PATCH 15/22] - setPlayBackSpeed renamed to setSpeed. - _applyPlayBackSpeed method renamed to _applySpeed --- packages/video_player/lib/video_player.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/video_player/lib/video_player.dart b/packages/video_player/lib/video_player.dart index 4f5f265835de..3ceb5845972a 100644 --- a/packages/video_player/lib/video_player.dart +++ b/packages/video_player/lib/video_player.dart @@ -5,8 +5,8 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:meta/meta.dart'; final MethodChannel _channel = const MethodChannel('flutter.io/videoPlayer') @@ -249,7 +249,7 @@ class VideoPlayerController extends ValueNotifier { _applyLooping(); _applyVolume(); _applyPlayPause(); - _applyPlayBackSpeed(); + _applySpeed(); break; case 'completed': value = value.copyWith(isPlaying: false, position: value.duration); @@ -363,7 +363,7 @@ class VideoPlayerController extends ValueNotifier { ); // Ensure the video is played at the correct speed - await _applyPlayBackSpeed(); + await _applySpeed(); } else { _timer?.cancel(); // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. @@ -433,7 +433,7 @@ class VideoPlayerController extends ValueNotifier { await _applyVolume(); } - Future _applyPlayBackSpeed() async { + Future _applySpeed() async { if (!value.initialized || _isDisposed) { return; } @@ -446,7 +446,7 @@ class VideoPlayerController extends ValueNotifier { // ignore: strong_mode_implicit_dynamic_method await _channel.invokeMethod( - 'setPlayBackSpeed', + 'setSpeed', {'textureId': _textureId, 'speed': value.speed}, ); } @@ -457,7 +457,7 @@ class VideoPlayerController extends ValueNotifier { /// by default speed value is 1.0 Future setSpeed(double speed) async { value = value.copyWith(speed: speed); - await _applyPlayBackSpeed(); + await _applySpeed(); } } From 3209ac01b1cf259ff8f707aec29ce8700ded6d21 Mon Sep 17 00:00:00 2001 From: Rodrigo Castro Date: Mon, 27 May 2019 09:56:59 +0100 Subject: [PATCH 16/22] Renamed setPlaybackSpeed to setSpeed on iOS --- packages/video_player/ios/Classes/VideoPlayerPlugin.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/video_player/ios/Classes/VideoPlayerPlugin.m b/packages/video_player/ios/Classes/VideoPlayerPlugin.m index 32a75c8ed4ba..d1ac32fd9ca1 100644 --- a/packages/video_player/ios/Classes/VideoPlayerPlugin.m +++ b/packages/video_player/ios/Classes/VideoPlayerPlugin.m @@ -342,7 +342,7 @@ - (void)setVolume:(double)volume { _player.volume = (volume < 0.0) ? 0.0 : ((volume > 1.0) ? 1.0 : volume); } -- (void)setPlayBackSpeed:(double)speed { +- (void)setSpeed:(double)speed { if (speed == 1.0 || speed == 0.0) { _player.rate = speed; } else if (speed < 0 || speed > 2.0) { @@ -507,8 +507,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([@"pause" isEqualToString:call.method]) { [player pause]; result(nil); - } else if ([@"setPlayBackSpeed" isEqualToString:call.method]) { - [player setPlayBackSpeed:[[argsMap objectForKey:@"speed"] doubleValue]]; + } else if ([@"setSpeed" isEqualToString:call.method]) { + [player setSpeed:[[argsMap objectForKey:@"speed"] doubleValue]]; result(nil); } else { result(FlutterMethodNotImplemented); From 1e7311d2ee7e024962e38e63d873f465353cc135 Mon Sep 17 00:00:00 2001 From: John Nair Date: Mon, 27 May 2019 16:34:01 +0530 Subject: [PATCH 17/22] CHANGELOG and pubspec file has been updated to 0.10.0+8 version --- packages/video_player/CHANGELOG.md | 2 +- packages/video_player/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/video_player/CHANGELOG.md b/packages/video_player/CHANGELOG.md index 9bbbce94ab16..8e5104f06e58 100644 --- a/packages/video_player/CHANGELOG.md +++ b/packages/video_player/CHANGELOG.md @@ -1,5 +1,5 @@ -## 0.10.0+7 +## 0.10.0+8 * Implemented playback speed feature. diff --git a/packages/video_player/pubspec.yaml b/packages/video_player/pubspec.yaml index 168a55448c9d..e469a120afa1 100644 --- a/packages/video_player/pubspec.yaml +++ b/packages/video_player/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player description: Flutter plugin for displaying inline video with other Flutter widgets on Android and iOS. author: Flutter Team -version: 0.10.0+7 +version: 0.10.0+8 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player flutter: From 86c1644fa705fa1f6cfd5e2c2e7e15e1d167e774 Mon Sep 17 00:00:00 2001 From: Axel LE BOT Date: Fri, 19 Jul 2019 09:33:53 +0200 Subject: [PATCH 18/22] [video_player] add type params for invokeMethod calls. - Bumped min flutter version to 1.2.0. - Added template type parameter for invokeMethod calls. - Updated invokeMethod and invokeMethod to using invokeListMethod and invokeMapMethod if any. --- packages/video_player/CHANGELOG.md | 2 + packages/video_player/lib/video_player.dart | 52 +++++---------------- packages/video_player/pubspec.yaml | 2 +- 3 files changed, 15 insertions(+), 41 deletions(-) diff --git a/packages/video_player/CHANGELOG.md b/packages/video_player/CHANGELOG.md index 4d20b9c3f913..18ede8d47e61 100644 --- a/packages/video_player/CHANGELOG.md +++ b/packages/video_player/CHANGELOG.md @@ -2,6 +2,8 @@ ## 0.10.1+2 * Implemented playback speed feature. +* Bump the minimum Flutter version to 1.2.0. +* Add template type parameter to `invokeMethod` calls. ## 0.10.1+1 diff --git a/packages/video_player/lib/video_player.dart b/packages/video_player/lib/video_player.dart index 3ceb5845972a..37397c40fcb3 100644 --- a/packages/video_player/lib/video_player.dart +++ b/packages/video_player/lib/video_player.dart @@ -10,12 +10,9 @@ import 'package:flutter/services.dart'; import 'package:meta/meta.dart'; final MethodChannel _channel = const MethodChannel('flutter.io/videoPlayer') - // This will clear all open videos on the platform when a full restart is - // performed. - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - ..invokeMethod('init'); +// This will clear all open videos on the platform when a full restart is +// performed. + ..invokeMethod('init'); class DurationRange { DurationRange(this.start, this.end); @@ -217,10 +214,7 @@ class VideoPlayerController extends ValueNotifier { case DataSourceType.file: dataSourceDescription = {'uri': dataSource}; } - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - final Map response = await _channel.invokeMethod( + final Map response = await _channel.invokeMapMethod( 'create', dataSourceDescription, ); @@ -294,10 +288,7 @@ class VideoPlayerController extends ValueNotifier { _isDisposed = true; _timer?.cancel(); await _eventSubscription?.cancel(); - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - await _channel.invokeMethod( + await _channel.invokeMethod( 'dispose', {'textureId': _textureId}, ); @@ -327,10 +318,7 @@ class VideoPlayerController extends ValueNotifier { if (!value.initialized || _isDisposed) { return; } - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - _channel.invokeMethod( + _channel.invokeMethod( 'setLooping', {'textureId': _textureId, 'looping': value.isLooping}, ); @@ -341,10 +329,7 @@ class VideoPlayerController extends ValueNotifier { return; } if (value.isPlaying) { - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - await _channel.invokeMethod( + await _channel.invokeMethod( 'play', {'textureId': _textureId}, ); @@ -366,10 +351,7 @@ class VideoPlayerController extends ValueNotifier { await _applySpeed(); } else { _timer?.cancel(); - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - await _channel.invokeMethod( + await _channel.invokeMethod( 'pause', {'textureId': _textureId}, ); @@ -380,10 +362,7 @@ class VideoPlayerController extends ValueNotifier { if (!value.initialized || _isDisposed) { return; } - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - await _channel.invokeMethod( + await _channel.invokeMethod( 'setVolume', {'textureId': _textureId, 'volume': value.volume}, ); @@ -395,10 +374,7 @@ class VideoPlayerController extends ValueNotifier { return null; } return Duration( - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - milliseconds: await _channel.invokeMethod( + milliseconds: await _channel.invokeMethod( 'position', {'textureId': _textureId}, ), @@ -414,10 +390,7 @@ class VideoPlayerController extends ValueNotifier { } else if (moment < const Duration()) { moment = const Duration(); } - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - await _channel.invokeMethod('seekTo', { + await _channel.invokeMethod('seekTo', { 'textureId': _textureId, 'location': moment.inMilliseconds, }); @@ -444,8 +417,7 @@ class VideoPlayerController extends ValueNotifier { return; } - // ignore: strong_mode_implicit_dynamic_method - await _channel.invokeMethod( + await _channel.invokeMethod( 'setSpeed', {'textureId': _textureId, 'speed': value.speed}, ); diff --git a/packages/video_player/pubspec.yaml b/packages/video_player/pubspec.yaml index 4142af1aa595..aedc3b4f10e6 100644 --- a/packages/video_player/pubspec.yaml +++ b/packages/video_player/pubspec.yaml @@ -22,4 +22,4 @@ dev_dependencies: environment: sdk: ">=2.0.0-dev.28.0 <3.0.0" - flutter: ">=0.2.5 <2.0.0" + flutter: ">=1.2.0 <2.0.0" From 7225730443026e656d79f0d2f6f1de1a6acc6240 Mon Sep 17 00:00:00 2001 From: Rodrigo Castro Date: Wed, 7 Aug 2019 19:00:42 +0100 Subject: [PATCH 19/22] Throw a FlutterError if the speed is unsupported in iOS. Fixes https://github.com/flutter/plugins/pull/1400/files/b6a35220ce049796fc4c6274227a3fb231d4b77c#diff-e534c460662d39cc715c4eed77a31c3f --- .../ios/Classes/VideoPlayerPlugin.m | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/video_player/ios/Classes/VideoPlayerPlugin.m b/packages/video_player/ios/Classes/VideoPlayerPlugin.m index 2fab5d16a9d9..a861df05626b 100644 --- a/packages/video_player/ios/Classes/VideoPlayerPlugin.m +++ b/packages/video_player/ios/Classes/VideoPlayerPlugin.m @@ -354,16 +354,29 @@ - (void)setVolume:(double)volume { _player.volume = (float)((volume < 0.0) ? 0.0 : ((volume > 1.0) ? 1.0 : volume)); } -- (void)setSpeed:(double)speed { +- (void)setSpeed:(double)speed result:(FlutterResult)result { if (speed == 1.0 || speed == 0.0) { _player.rate = speed; + result(nil); } else if (speed < 0 || speed > 2.0) { - NSLog(@"Speed outside supported range %f", speed); + result([FlutterError + errorWithCode:@"unsupported_speed" + message:@"Speed must be greater than or equal to 0.0 and less than 2.0" + details:nil]); } else if ((speed > 1.0 && _player.currentItem.canPlayFastForward) || (speed < 1.0 && _player.currentItem.canPlaySlowForward)) { _player.rate = speed; + result(nil); } else { - NSLog(@"Unsupported speed. Cannot play fast/slow forward: %f", speed); + if (speed > 1.0) { + result([FlutterError errorWithCode:@"unsupported_fast_forward" + message:@"This video cannot be played fast forward" + details:nil]); + } else { + result([FlutterError errorWithCode:@"unsupported_slow_forward" + message:@"This video cannot be played slow forward" + details:nil]); + } } } @@ -520,8 +533,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [player pause]; result(nil); } else if ([@"setSpeed" isEqualToString:call.method]) { - [player setSpeed:[[argsMap objectForKey:@"speed"] doubleValue]]; - result(nil); + [player setSpeed:[[argsMap objectForKey:@"speed"] doubleValue] result:result]; + return; } else { result(FlutterMethodNotImplemented); } From ebd97e9879f1512f1acf64f76f6dd09c4a1cae5b Mon Sep 17 00:00:00 2001 From: Rodrigo Castro Date: Wed, 7 Aug 2019 19:08:55 +0100 Subject: [PATCH 20/22] - Improve documentation for supported speeds in video_player - Improve comment on why setSpeed is not called when the video is not playing --- packages/video_player/ios/Classes/VideoPlayerPlugin.m | 2 +- packages/video_player/lib/video_player.dart | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/video_player/ios/Classes/VideoPlayerPlugin.m b/packages/video_player/ios/Classes/VideoPlayerPlugin.m index a861df05626b..81105fb03cdf 100644 --- a/packages/video_player/ios/Classes/VideoPlayerPlugin.m +++ b/packages/video_player/ios/Classes/VideoPlayerPlugin.m @@ -361,7 +361,7 @@ - (void)setSpeed:(double)speed result:(FlutterResult)result { } else if (speed < 0 || speed > 2.0) { result([FlutterError errorWithCode:@"unsupported_speed" - message:@"Speed must be greater than or equal to 0.0 and less than 2.0" + message:@"Speed must be >= 0.0 and <= 2.0" details:nil]); } else if ((speed > 1.0 && _player.currentItem.canPlayFastForward) || (speed < 1.0 && _player.currentItem.canPlaySlowForward)) { diff --git a/packages/video_player/lib/video_player.dart b/packages/video_player/lib/video_player.dart index 226bca1bded5..3e1b6ee77dd4 100644 --- a/packages/video_player/lib/video_player.dart +++ b/packages/video_player/lib/video_player.dart @@ -416,8 +416,10 @@ class VideoPlayerController extends ValueNotifier { return; } - // On iOS setting the speed will start playing the video automatically - // Do not change the video speed until after the video is played + // On iOS setting the speed on an AVPlayer starts playing + // the video straightaway. We avoid this surprising behaviour + // by not changing the speed of the player until after the video + // starts playing if (!value.isPlaying) { return; } @@ -432,6 +434,9 @@ class VideoPlayerController extends ValueNotifier { /// /// [speed] can be 0.5x, 1x, 2x /// by default speed value is 1.0 + /// + /// Negative speeds are not supported + /// speeds above 2x are not supported on iOS Future setSpeed(double speed) async { value = value.copyWith(speed: speed); await _applySpeed(); From f48cccc3b07b1300dd797bf33f05395948337568 Mon Sep 17 00:00:00 2001 From: Rodrigo Castro Date: Fri, 9 Aug 2019 00:47:49 +0100 Subject: [PATCH 21/22] Format files --- packages/video_player/ios/Classes/VideoPlayerPlugin.m | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/video_player/ios/Classes/VideoPlayerPlugin.m b/packages/video_player/ios/Classes/VideoPlayerPlugin.m index 81105fb03cdf..5014247d1e3b 100644 --- a/packages/video_player/ios/Classes/VideoPlayerPlugin.m +++ b/packages/video_player/ios/Classes/VideoPlayerPlugin.m @@ -359,10 +359,9 @@ - (void)setSpeed:(double)speed result:(FlutterResult)result { _player.rate = speed; result(nil); } else if (speed < 0 || speed > 2.0) { - result([FlutterError - errorWithCode:@"unsupported_speed" - message:@"Speed must be >= 0.0 and <= 2.0" - details:nil]); + result([FlutterError errorWithCode:@"unsupported_speed" + message:@"Speed must be >= 0.0 and <= 2.0" + details:nil]); } else if ((speed > 1.0 && _player.currentItem.canPlayFastForward) || (speed < 1.0 && _player.currentItem.canPlaySlowForward)) { _player.rate = speed; From c9a68a1d883ca39e475afcd5e1353977be1640e4 Mon Sep 17 00:00:00 2001 From: John Nair <48906398+JONA-Cureambit@users.noreply.github.com> Date: Mon, 12 Aug 2019 19:57:08 +0530 Subject: [PATCH 22/22] Update CHANGELOG.md --- packages/video_player/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/CHANGELOG.md b/packages/video_player/CHANGELOG.md index 79f6ca830e69..68393fd47db3 100644 --- a/packages/video_player/CHANGELOG.md +++ b/packages/video_player/CHANGELOG.md @@ -3,7 +3,7 @@ * Implemented playback speed feature. * Bump the minimum Flutter version to 1.2.0. * Add template type parameter to `invokeMethod` calls. -======= + ## 0.10.1+6 * [iOS] Fixed a memory leak with notification observing.