@@ -82,6 +82,8 @@ @interface FVPVideoPlayer ()
82
82
@property (nonatomic ) CGAffineTransform preferredTransform;
83
83
@property (nonatomic , readonly ) BOOL disposed;
84
84
@property (nonatomic , readonly ) BOOL isPlaying;
85
+ // Playback speed when video is playing.
86
+ @property (nonatomic , readonly ) NSNumber *playbackSpeed;
85
87
@property (nonatomic ) BOOL isLooping;
86
88
@property (nonatomic , readonly ) BOOL isInitialized;
87
89
// The updater that drives callbacks to the engine to indicate that a new frame is ready.
@@ -397,7 +399,15 @@ - (void)updatePlayingState {
397
399
return ;
398
400
}
399
401
if (_isPlaying) {
400
- [_player play ];
402
+ // Calling play is the same as setting the rate to 1.0 (or to defaultRate depending on ios
403
+ // version) so last set playback speed must be set here if any instead.
404
+ // https://github.com/flutter/flutter/issues/71264
405
+ // https://github.com/flutter/flutter/issues/73643
406
+ if (_playbackSpeed) {
407
+ [self updateRate ];
408
+ } else {
409
+ [_player play ];
410
+ }
401
411
} else {
402
412
[_player pause ];
403
413
}
@@ -406,6 +416,29 @@ - (void)updatePlayingState {
406
416
_displayLink.running = _isPlaying || self.waitingForFrame ;
407
417
}
408
418
419
+ - (void )updateRate {
420
+ // See https://developer.apple.com/library/archive/qa/qa1772/_index.html for an explanation of
421
+ // these checks.
422
+ // If status is not AVPlayerItemStatusReadyToPlay then both canPlayFastForward
423
+ // and canPlaySlowForward are always false and it is unknown whether video can
424
+ // be played at these speeds, updatePlayingState will be called again when
425
+ // status changes to AVPlayerItemStatusReadyToPlay.
426
+ float speed = _playbackSpeed.floatValue ;
427
+ if (speed > 2.0 && !_player.currentItem .canPlayFastForward ) {
428
+ if (_player.currentItem .status != AVPlayerItemStatusReadyToPlay) {
429
+ return ;
430
+ }
431
+ speed = 2.0 ;
432
+ }
433
+ if (speed < 1.0 && !_player.currentItem .canPlaySlowForward ) {
434
+ if (_player.currentItem .status != AVPlayerItemStatusReadyToPlay) {
435
+ return ;
436
+ }
437
+ speed = 1.0 ;
438
+ }
439
+ _player.rate = speed;
440
+ }
441
+
409
442
- (void )setupEventSinkIfReadyToPlay {
410
443
if (_eventSink && !_isInitialized) {
411
444
AVPlayerItem *currentItem = self.player .currentItem ;
@@ -519,27 +552,8 @@ - (void)setVolume:(double)volume {
519
552
}
520
553
521
554
- (void )setPlaybackSpeed : (double )speed {
522
- // See https://developer.apple.com/library/archive/qa/qa1772/_index.html for an explanation of
523
- // these checks.
524
- if (speed > 2.0 && !_player.currentItem .canPlayFastForward ) {
525
- if (_eventSink != nil ) {
526
- _eventSink ([FlutterError errorWithCode: @" VideoError"
527
- message: @" Video cannot be fast-forwarded beyond 2.0x"
528
- details: nil ]);
529
- }
530
- return ;
531
- }
532
-
533
- if (speed < 1.0 && !_player.currentItem .canPlaySlowForward ) {
534
- if (_eventSink != nil ) {
535
- _eventSink ([FlutterError errorWithCode: @" VideoError"
536
- message: @" Video cannot be slow-forwarded"
537
- details: nil ]);
538
- }
539
- return ;
540
- }
541
-
542
- _player.rate = speed;
555
+ _playbackSpeed = @(speed);
556
+ [self updatePlayingState ];
543
557
}
544
558
545
559
- (CVPixelBufferRef)copyPixelBuffer {
0 commit comments