diff --git a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m index f35d38bd9895..dc255fb7589c 100644 --- a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m @@ -839,6 +839,60 @@ - (void)testFailedToLoadVideoEventShouldBeAlwaysSent { [self waitForExpectationsWithTimeout:10.0 handler:nil]; } +- (void)testPlaybackSpeedRemainsAfterSeek { + NSObject *mockTextureRegistry = + OCMProtocolMock(@protocol(FlutterTextureRegistry)); + NSObject *registrar = + [GetPluginRegistry() registrarForPlugin:@"PlaybackSpeedRemainsAfterSeek"]; + NSObject *partialRegistrar = OCMPartialMock(registrar); + OCMStub([partialRegistrar textures]).andReturn(mockTextureRegistry); + + // Create mock video output and AVPlayer + AVPlayerItemVideoOutput *mockVideoOutput = OCMPartialMock([[AVPlayerItemVideoOutput alloc] init]); + StubAVPlayer *mockAVPlayer = [[StubAVPlayer alloc] init]; + + // Set up the player factory + FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc] + initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:mockAVPlayer output:mockVideoOutput] + displayLinkFactory:nil + registrar:partialRegistrar]; + + // Initialize the video player plugin + FlutterError *initError; + [videoPlayerPlugin initialize:&initError]; + XCTAssertNil(initError); + + FVPCreationOptions *create = [FVPCreationOptions makeWithAsset:nil + uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" + packageName:nil + formatHint:nil + httpHeaders:@{}]; + FlutterError *createError; + NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&createError]; + XCTAssertNil(createError); + + // Ensure player was created + FVPVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId]; + XCTAssertNotNil(player); + AVPlayer *avPlayer = player.player; + + // Set playback speed to 2.0 + [player setPlaybackSpeed:2.0]; + XCTAssertEqual(avPlayer.rate, 2.0, @"Playback speed should be set to 2.0"); + + // Perform a seek operation + [videoPlayerPlugin seekTo:1234 + forPlayer:textureId.integerValue + completion:^(FlutterError *_Nullable error) { + XCTAssertNil(error); + }]; + + // After seek, verify playback speed remains unchanged + XCTAssertEqual(avPlayer.rate, 2.0, @"Playback speed should remain 2.0 after seeking"); + XCTAssertEqual(avPlayer.defaultRate, 2.0, @"Default playback speed should be 2.0 after seeking"); +} + + #if TARGET_OS_IOS - (void)validateTransformFixForOrientation:(UIImageOrientation)orientation { AVAssetTrack *track = [[FakeAVAssetTrack alloc] initWithOrientation:orientation]; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m index 5892274a37db..32161195eb23 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m @@ -492,7 +492,10 @@ - (void)setPlaybackSpeed:(double)speed { } return; } - + if (@available(iOS 16, *)) { + // this will avoid resetting playback speed to 1.0x on seeking + _player.defaultRate = speed; + } _player.rate = speed; } 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 dac0553fbed0..e4e8cd08f85b 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 @@ -374,5 +374,20 @@ void main() { ), ])); }); + + test('playback speed should remain after seeking', () async { + // Set initial playback speed + await player.setPlaybackSpeed(1, 2.0); // Set to 2.0x + expect(log.log.last, 'setPlaybackSpeed'); + expect(log.playbackSpeed, 2.0); + + // Now seek to a different position + await player.seekTo(1, const Duration(milliseconds: 5000)); // Seek to 5 seconds + expect(log.log.last, 'seekTo'); + expect(log.position, 5000); + + // After seeking, the playback speed should still be 2.0x + expect(log.playbackSpeed, 2.0); + }); }); }