Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[video_player_avfoundation] fix playback speed resetting #7657

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

misos1
Copy link
Contributor

@misos1 misos1 commented Sep 17, 2024

Calling play is the same as setting the rate to 1.0 (or to defaultRate depending on ios version, documentation in this first link is clearly not updated because it does not mention defaultRate):

https://developer.apple.com/documentation/avfoundation/avplayer/1386726-play?language=objc
https://developer.apple.com/documentation/avfoundation/avplayer/3929373-defaultrate?language=objc

The second link contains a note about not starting playback by setting the rate to 1.0. I assume this is because of the introduction of defaultRate (which can be different than 1.0) and not because play may do something more than just setting rate as that wording is explicit about setting rate to 1.0, it says nothing about any other value.

This is also why flutter/plugins#4331 did not work well. It was setting rate to 1.0 (through play) then immediately to the value set by setPlaybackSpeed. One of them or both caused change to playbackLikelyToKeepUp which triggered observeValueForKeyPath with playbackLikelyToKeepUpContext which in turn called again updatePlayingState where was rate again changed first to 1.0 then to another value and so on. In this PR the rate is changed only once and then to the same value, seems when rate is assigned but not really changed it does not trigger anything.

In issues below seekTo can trigger playbackLikelyToKeepUp change which will call updatePlayingState and reset rate to 1.0 through play. When the speed is set in dart before initialization then the dart side will set it right after calling play but even some time after initialization there is still a flood of events calling updatePlayingState and it is likely that some of them will call it after setPlaybackSpeed. Actually even when setPlaybackSpeed was not called on dart side it (dart side) will always change speed after play to 1.0 so it ignores this new defaultRate feature.

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@misos1 misos1 changed the title [video_player_avfoundation, video_player] fix playback speed resetting [video_player_avfoundation] fix playback speed resetting Sep 18, 2024
@misos1 misos1 marked this pull request as ready for review September 18, 2024 16:14
@stuartmorgan
Copy link
Contributor

Sorry, this fell off my review queue. @hellohuanlin can you take a look as well?

// status changes to AVPlayerItemStatusReadyToPlay.
float speed = _playbackSpeed.floatValue;
if (speed > 2.0 && !_player.currentItem.canPlayFastForward) {
if (_player.currentItem.status != AVPlayerItemStatusReadyToPlay) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if we call updateRate with speed < 2.0? should we also check the status here? Should this status check be moved to top of the fucntion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That check is to confirm whether canPlay... returned false because it cannot be played at speed>2 instead of because status was not ready to play. There is no need to check status for speeds for which is not needed canPlay.... But as I am thinking about it now maybe status should be fetched before canPlay... to avoid a false positive when status becomes ready right after canPlay....

Copy link
Contributor

@stuartmorgan stuartmorgan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic and tests all LGTM, just small comments on naming and style guide conformance.

@@ -397,7 +399,15 @@ - (void)updatePlayingState {
return;
}
if (_isPlaying) {
[_player play];
// Calling play is the same as setting the rate to 1.0 (or to defaultRate depending on ios
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: iOS

// be played at these speeds, updatePlayingState will be called again when
// status changes to AVPlayerItemStatusReadyToPlay.
float speed = _playbackSpeed.floatValue;
bool readyToPlay = _player.currentItem.status == AVPlayerItemStatusReadyToPlay;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BOOL since this is Obj-C code.

@@ -82,6 +82,8 @@ @interface FVPVideoPlayer ()
@property(nonatomic) CGAffineTransform preferredTransform;
@property(nonatomic, readonly) BOOL disposed;
@property(nonatomic, readonly) BOOL isPlaying;
// Playback speed when video is playing.
@property(nonatomic, readonly) NSNumber *playbackSpeed;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more accurate to call this targetPlaybackSpeed (and document it as something like "The target playback speed requested by the plugin client."), since it's entirely possible for the playback speed to not be this value while playing.

@@ -406,6 +416,30 @@ - (void)updatePlayingState {
_displayLink.running = _isPlaying || self.waitingForFrame;
}

- (void)updateRate {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a declaration comment, per the style guide.

@stuartmorgan stuartmorgan added the triage-ios Should be looked at in iOS triage label Nov 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants