Skip to content

Commit a75e906

Browse files
committed
fix playback speed resetting
1 parent 03623b6 commit a75e906

File tree

4 files changed

+76
-24
lines changed

4 files changed

+76
-24
lines changed

packages/video_player/video_player_avfoundation/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 2.6.2
22

3+
* Fixes playback speed resetting.
34
* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3.
45

56
## 2.6.1

packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,8 @@ - (void)testSeekToleranceWhenSeekingToEnd {
656656
// Change playback speed.
657657
[videoPlayerPlugin setPlaybackSpeed:2 forPlayer:textureId.integerValue error:&error];
658658
XCTAssertNil(error);
659+
[videoPlayerPlugin playPlayer:textureId.integerValue error:&error];
660+
XCTAssertNil(error);
659661
XCTAssertEqual(avPlayer.rate, 2);
660662
XCTAssertEqual(avPlayer.timeControlStatus, AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate);
661663

@@ -790,6 +792,41 @@ - (void)testPublishesInRegistration {
790792
XCTAssertTrue([publishedValue isKindOfClass:[FVPVideoPlayerPlugin class]]);
791793
}
792794

795+
- (void)testUpdatePlayingStateShouldNotResetRate {
796+
NSObject<FlutterPluginRegistrar> *registrar =
797+
[GetPluginRegistry() registrarForPlugin:@"testUpdatePlayingStateShouldNotResetRate"];
798+
799+
FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc]
800+
initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:nil]
801+
displayLinkFactory:nil
802+
registrar:registrar];
803+
804+
FlutterError *error;
805+
[videoPlayerPlugin initialize:&error];
806+
XCTAssertNil(error);
807+
FVPCreationOptions *create = [FVPCreationOptions
808+
makeWithAsset:nil
809+
uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4"
810+
packageName:nil
811+
formatHint:nil
812+
httpHeaders:@{}];
813+
NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&error];
814+
FVPVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId];
815+
816+
XCTestExpectation *initializedExpectation = [self expectationWithDescription:@"initialized"];
817+
[player onListenWithArguments:nil
818+
eventSink:^(NSDictionary<NSString *, id> *event) {
819+
if ([event[@"event"] isEqualToString:@"initialized"]) {
820+
[initializedExpectation fulfill];
821+
}
822+
}];
823+
[self waitForExpectationsWithTimeout:10 handler:nil];
824+
825+
[videoPlayerPlugin setPlaybackSpeed:2 forPlayer:textureId.integerValue error:&error];
826+
[videoPlayerPlugin playPlayer:textureId.integerValue error:&error];
827+
XCTAssertEqual(player.player.rate, 2);
828+
}
829+
793830
#if TARGET_OS_IOS
794831
- (void)validateTransformFixForOrientation:(UIImageOrientation)orientation {
795832
AVAssetTrack *track = [[FakeAVAssetTrack alloc] initWithOrientation:orientation];

packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ @interface FVPVideoPlayer ()
8282
@property(nonatomic) CGAffineTransform preferredTransform;
8383
@property(nonatomic, readonly) BOOL disposed;
8484
@property(nonatomic, readonly) BOOL isPlaying;
85+
// Playback speed when video is playing.
86+
@property(nonatomic, readonly) NSNumber *playbackSpeed;
8587
@property(nonatomic) BOOL isLooping;
8688
@property(nonatomic, readonly) BOOL isInitialized;
8789
// The updater that drives callbacks to the engine to indicate that a new frame is ready.
@@ -397,7 +399,15 @@ - (void)updatePlayingState {
397399
return;
398400
}
399401
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+
}
401411
} else {
402412
[_player pause];
403413
}
@@ -406,6 +416,29 @@ - (void)updatePlayingState {
406416
_displayLink.running = _isPlaying || self.waitingForFrame;
407417
}
408418

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+
409442
- (void)setupEventSinkIfReadyToPlay {
410443
if (_eventSink && !_isInitialized) {
411444
AVPlayerItem *currentItem = self.player.currentItem;
@@ -519,27 +552,8 @@ - (void)setVolume:(double)volume {
519552
}
520553

521554
- (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];
543557
}
544558

545559
- (CVPixelBufferRef)copyPixelBuffer {

packages/video_player/video_player_avfoundation/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: video_player_avfoundation
22
description: iOS and macOS implementation of the video_player plugin.
33
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
5-
version: 2.6.1
5+
version: 2.6.2
66

77
environment:
88
sdk: ^3.3.0

0 commit comments

Comments
 (0)