From f3d30e4cf52ebe9816936691c613a6db86714797 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 31 Jul 2025 08:33:07 -0400 Subject: [PATCH 1/7] Move KVO to flexible static helpers --- .../FVPTextureBasedVideoPlayer.m | 8 -- .../FVPVideoPlayer.m | 130 ++++++++++-------- 2 files changed, 74 insertions(+), 64 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPTextureBasedVideoPlayer.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPTextureBasedVideoPlayer.m index 425fd6f7af8..52e90a4fb42 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPTextureBasedVideoPlayer.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPTextureBasedVideoPlayer.m @@ -119,14 +119,6 @@ - (void)seekTo:(NSInteger)position completion:(void (^)(FlutterError *_Nullable) } - (void)dispose { - // This check prevents the crash caused by removing the KVO observers twice. - // When performing a Hot Restart, the leftover players are disposed once directly - // by [FVPVideoPlayerPlugin initialize:] method and then disposed again by - // [FVPVideoPlayer onTextureUnregistered:] call leading to possible over-release. - if (self.disposed) { - return; - } - [super dispose]; [self.playerLayer removeFromSuperlayer]; 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 4db671b8ab9..a4abd5ca731 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 @@ -16,7 +16,51 @@ static void *playbackLikelyToKeepUpContext = &playbackLikelyToKeepUpContext; static void *rateContext = &rateContext; -@implementation FVPVideoPlayer +/// Registers KVO observers on 'object' for each entry in 'observations', which must be a +/// dictionary mapping KVO keys to NSValue-wrapped context pointers. +static void FVPRegisterKeyValueObservers(NSObject *observer, + NSDictionary *observations, + NSObject *target) { + // TODO(stuartmorgan): Investigate why NSKeyValueObservingOptionInitial is being used here. This + // code is called from the player's init method, whith self as the observer, and + // NSKeyValueObservingOptionInitial is documented to synchronously call the observer during + // the registration, so this is causing invocations to self during init, which is dangerous. + // Most of the change handlers are just calling a listener that's not even set up yet, so many + // of the calls will not do anything useful anyway. Ideally NSKeyValueObservingOptionInitial + // should be removed, and if the initialization code potentially needs to be triggered that should + // be done explicitly. + for (NSString *key in observations) { + [target addObserver:observer + forKeyPath:key + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:observations[key].pointerValue]; + } +} + +/// Registers KVO observers on 'object' for each entry in 'observations', which must be a +/// dictionary mapping KVO keys to NSValue-wrapped context pointers. +/// +/// This should only be called to balance calls to FVPRegisterKeyValueObservers, as it is an +/// error to try to remove observers that are not currently set. +/// +/// This does not call any methods on 'observer', so is safe to call from 'obsever's dealloc. +static void FVPRemoveKeyValueObservers(NSObject *observer, + NSDictionary *observations, + NSObject *target) { + for (NSString *key in observations) { + [target removeObserver:observer forKeyPath:key]; + } +} + +@implementation FVPVideoPlayer { + // A mapping of KVO keys to NSValue-wrapped observer context pointers for observations that should + // be set for the AVPlayer instance. + NSDictionary *_playerObservations; + + // A mapping of KVO keys to NSValue-wrapped observer context pointers for observations that + // should be set for the AVPlayer instance's current AVPlayerItem. + NSDictionary *_playerItemObservations; +} - (instancetype)initWithURL:(NSURL *)url httpHeaders:(nonnull NSDictionary *)headers @@ -82,7 +126,23 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item }; _videoOutput = [avFactory videoOutputWithPixelBufferAttributes:pixBuffAttributes]; - [self addObserversForItem:item player:_player]; + // Set up all necessary observers to report video events. + _playerItemObservations = @{ + @"loadedTimeRanges" : [NSValue valueWithPointer:timeRangeContext], + @"status" : [NSValue valueWithPointer:statusContext], + @"presentationSize" : [NSValue valueWithPointer:presentationSizeContext], + @"duration" : [NSValue valueWithPointer:durationContext], + @"playbackLikelyToKeepUp" : [NSValue valueWithPointer:playbackLikelyToKeepUpContext], + }; + _playerObservations = @{ + @"rate" : [NSValue valueWithPointer:rateContext], + }; + FVPRegisterKeyValueObservers(self, _playerItemObservations, item); + FVPRegisterKeyValueObservers(self, _playerObservations, _player); + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(itemDidPlayToEndTime:) + name:AVPlayerItemDidPlayToEndTimeNotification + object:item]; [asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ] completionHandler:assetCompletionHandler]; @@ -90,17 +150,24 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item } - (void)dealloc { - if (!_disposed) { - [self removeKeyValueObservers]; - } + // In case dispose was never called for some reason, remove any remaining observers to prevent + // crashes. + FVPRemoveKeyValueObservers(self, _playerItemObservations, _player.currentItem); + FVPRemoveKeyValueObservers(self, _playerObservations, _player); } - (void)dispose { _disposed = YES; - [self removeKeyValueObservers]; - [self.player replaceCurrentItemWithPlayerItem:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self]; + FVPRemoveKeyValueObservers(self, _playerItemObservations, self.player.currentItem); + FVPRemoveKeyValueObservers(self, _playerObservations, self.player); + // Clear the list of observations, so that dealloc (or potential duplicate dispose calls, in the + // case of hot restart) don't try to re-remove them, which would be an error. + _playerItemObservations = @{}; + _playerObservations = @{}; + + [self.player replaceCurrentItemWithPlayerItem:nil]; if (_onDisposed) { _onDisposed(); @@ -108,42 +175,6 @@ - (void)dispose { [self.eventListener videoPlayerWasDisposed]; } -- (void)addObserversForItem:(AVPlayerItem *)item player:(AVPlayer *)player { - [item addObserver:self - forKeyPath:@"loadedTimeRanges" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:timeRangeContext]; - [item addObserver:self - forKeyPath:@"status" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:statusContext]; - [item addObserver:self - forKeyPath:@"presentationSize" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:presentationSizeContext]; - [item addObserver:self - forKeyPath:@"duration" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:durationContext]; - [item addObserver:self - forKeyPath:@"playbackLikelyToKeepUp" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:playbackLikelyToKeepUpContext]; - - // Add observer to AVPlayer instead of AVPlayerItem since the AVPlayerItem does not have a "rate" - // property - [player addObserver:self - forKeyPath:@"rate" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:rateContext]; - - // Add an observer that will respond to itemDidPlayToEndTime - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(itemDidPlayToEndTime:) - name:AVPlayerItemDidPlayToEndTimeNotification - object:item]; -} - - (void)itemDidPlayToEndTime:(NSNotification *)notification { if (_isLooping) { AVPlayerItem *p = [notification object]; @@ -436,17 +467,4 @@ - (int64_t)duration { return FVPCMTimeToMillis([[[_player currentItem] asset] duration]); } -/// Removes all key-value observers set up for the player. -/// -/// This is called from dealloc, so must not use any methods on self. -- (void)removeKeyValueObservers { - AVPlayerItem *currentItem = _player.currentItem; - [currentItem removeObserver:self forKeyPath:@"status"]; - [currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; - [currentItem removeObserver:self forKeyPath:@"presentationSize"]; - [currentItem removeObserver:self forKeyPath:@"duration"]; - [currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"]; - [_player removeObserver:self forKeyPath:@"rate"]; -} - @end From f4e17b6f035ea058ced5e0ee281f322a1fcd7f6c Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 31 Jul 2025 08:34:29 -0400 Subject: [PATCH 2/7] Remove KVO initial calls --- .../video_player_avfoundation/FVPVideoPlayer.m | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) 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 a4abd5ca731..4ce2f7ceeba 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 @@ -18,21 +18,19 @@ /// Registers KVO observers on 'object' for each entry in 'observations', which must be a /// dictionary mapping KVO keys to NSValue-wrapped context pointers. +/// +/// This does not call any methods on 'observer', so is safe to call from 'observer's init. static void FVPRegisterKeyValueObservers(NSObject *observer, NSDictionary *observations, NSObject *target) { - // TODO(stuartmorgan): Investigate why NSKeyValueObservingOptionInitial is being used here. This - // code is called from the player's init method, whith self as the observer, and - // NSKeyValueObservingOptionInitial is documented to synchronously call the observer during - // the registration, so this is causing invocations to self during init, which is dangerous. - // Most of the change handlers are just calling a listener that's not even set up yet, so many - // of the calls will not do anything useful anyway. Ideally NSKeyValueObservingOptionInitial - // should be removed, and if the initialization code potentially needs to be triggered that should - // be done explicitly. + // It is important not to use NSKeyValueObservingOptionInitial here, because that will cause + // synchronous calls to 'observer', violating the requirement that this method does not call its + // methods. If there are use cases for specific pieces of initial state, those should be handled + // explicitly by the caller, rather than by adding initial-state KVO notifications here. for (NSString *key in observations) { [target addObserver:observer forKeyPath:key - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + options:NSKeyValueObservingOptionNew context:observations[key].pointerValue]; } } From ffecb8518126285e117b98c7a1cedb62fb2336d9 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 31 Jul 2025 08:42:36 -0400 Subject: [PATCH 3/7] Version bump --- packages/video_player/video_player_avfoundation/CHANGELOG.md | 4 ++++ packages/video_player/video_player_avfoundation/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index e2afaf91d44..982b1ae65fa 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.3 + +* Removes calls to self from init and dealloc, for maintainability. + ## 2.8.2 * Restructure internals of Dart notification of video player events. diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index c2e2ece35e4..a9d17a56b67 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS and macOS implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.8.2 +version: 2.8.3 environment: sdk: ^3.6.0 From cef5691d2f3328fefe72a72831de00e615268939 Mon Sep 17 00:00:00 2001 From: stuartmorgan-g Date: Fri, 1 Aug 2025 10:02:13 -0400 Subject: [PATCH 4/7] Typo fix Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../Sources/video_player_avfoundation/FVPVideoPlayer.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4ce2f7ceeba..9ad80cb71af 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 @@ -41,7 +41,7 @@ static void FVPRegisterKeyValueObservers(NSObject *observer, /// This should only be called to balance calls to FVPRegisterKeyValueObservers, as it is an /// error to try to remove observers that are not currently set. /// -/// This does not call any methods on 'observer', so is safe to call from 'obsever's dealloc. +/// This does not call any methods on 'observer', so is safe to call from 'observer's dealloc. static void FVPRemoveKeyValueObservers(NSObject *observer, NSDictionary *observations, NSObject *target) { From f9b266fa89f9b0a9e9e0f602b920460a5d9ff9e5 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Mon, 4 Aug 2025 14:34:21 -0400 Subject: [PATCH 5/7] Use _disposed instead of modifying registration lists --- .../FVPVideoPlayer.m | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) 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 9ad80cb71af..5646834dbd2 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 @@ -50,16 +50,28 @@ static void FVPRemoveKeyValueObservers(NSObject *observer, } } -@implementation FVPVideoPlayer { - // A mapping of KVO keys to NSValue-wrapped observer context pointers for observations that should - // be set for the AVPlayer instance. - NSDictionary *_playerObservations; - - // A mapping of KVO keys to NSValue-wrapped observer context pointers for observations that - // should be set for the AVPlayer instance's current AVPlayerItem. - NSDictionary *_playerItemObservations; +/// Returns a mapping of KVO keys to NSValue-wrapped observer context pointers for observations that +/// should be set for AVPlayer instances. +static NSDictionary *FVPGetPlayerObservations() { + return @{ + @"rate" : [NSValue valueWithPointer:rateContext], + }; } +/// Returns a mapping of KVO keys to NSValue-wrapped observer context pointers for observations that +/// should be set for AVPlayerItem instances. +static NSDictionary *FVPGetPlayerItemObservations() { + return @{ + @"loadedTimeRanges" : [NSValue valueWithPointer:timeRangeContext], + @"status" : [NSValue valueWithPointer:statusContext], + @"presentationSize" : [NSValue valueWithPointer:presentationSizeContext], + @"duration" : [NSValue valueWithPointer:durationContext], + @"playbackLikelyToKeepUp" : [NSValue valueWithPointer:playbackLikelyToKeepUpContext], + }; +} + +@implementation FVPVideoPlayer + - (instancetype)initWithURL:(NSURL *)url httpHeaders:(nonnull NSDictionary *)headers avFactory:(id)avFactory @@ -125,18 +137,8 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item _videoOutput = [avFactory videoOutputWithPixelBufferAttributes:pixBuffAttributes]; // Set up all necessary observers to report video events. - _playerItemObservations = @{ - @"loadedTimeRanges" : [NSValue valueWithPointer:timeRangeContext], - @"status" : [NSValue valueWithPointer:statusContext], - @"presentationSize" : [NSValue valueWithPointer:presentationSizeContext], - @"duration" : [NSValue valueWithPointer:durationContext], - @"playbackLikelyToKeepUp" : [NSValue valueWithPointer:playbackLikelyToKeepUpContext], - }; - _playerObservations = @{ - @"rate" : [NSValue valueWithPointer:rateContext], - }; - FVPRegisterKeyValueObservers(self, _playerItemObservations, item); - FVPRegisterKeyValueObservers(self, _playerObservations, _player); + FVPRegisterKeyValueObservers(self, FVPGetPlayerItemObservations(), item); + FVPRegisterKeyValueObservers(self, FVPGetPlayerObservations(), _player); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidPlayToEndTime:) name:AVPlayerItemDidPlayToEndTimeNotification @@ -148,22 +150,23 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item } - (void)dealloc { - // In case dispose was never called for some reason, remove any remaining observers to prevent - // crashes. - FVPRemoveKeyValueObservers(self, _playerItemObservations, _player.currentItem); - FVPRemoveKeyValueObservers(self, _playerObservations, _player); + if (!_disposed) { + // If dispose was never called for some reason, remove observers to prevent crashes. + FVPRemoveKeyValueObservers(self, FVPGetPlayerItemObservations(), _player.currentItem); + FVPRemoveKeyValueObservers(self, FVPGetPlayerObservations(), _player); + } } - (void)dispose { + // In some hot restart scenarios, dispose can be called twice, so no-op after the first time. + if (_disposed) { + return; + } _disposed = YES; [[NSNotificationCenter defaultCenter] removeObserver:self]; - FVPRemoveKeyValueObservers(self, _playerItemObservations, self.player.currentItem); - FVPRemoveKeyValueObservers(self, _playerObservations, self.player); - // Clear the list of observations, so that dealloc (or potential duplicate dispose calls, in the - // case of hot restart) don't try to re-remove them, which would be an error. - _playerItemObservations = @{}; - _playerObservations = @{}; + FVPRemoveKeyValueObservers(self, FVPGetPlayerItemObservations(), self.player.currentItem); + FVPRemoveKeyValueObservers(self, FVPGetPlayerObservations(), self.player); [self.player replaceCurrentItemWithPlayerItem:nil]; From 9ad7fe7fe54c390e2e83830b8b4b2d37efe2bec1 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 5 Aug 2025 09:42:05 -0400 Subject: [PATCH 6/7] Move listener registration out of init --- .../FVPVideoPlayer.m | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) 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 5646834dbd2..ffd3891d52c 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 @@ -70,7 +70,10 @@ static void FVPRemoveKeyValueObservers(NSObject *observer, }; } -@implementation FVPVideoPlayer +@implementation FVPVideoPlayer { + // Whether or not player and player item listeners have ever been registered. + BOOL _listenersRegistered; +} - (instancetype)initWithURL:(NSURL *)url httpHeaders:(nonnull NSDictionary *)headers @@ -136,21 +139,13 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item }; _videoOutput = [avFactory videoOutputWithPixelBufferAttributes:pixBuffAttributes]; - // Set up all necessary observers to report video events. - FVPRegisterKeyValueObservers(self, FVPGetPlayerItemObservations(), item); - FVPRegisterKeyValueObservers(self, FVPGetPlayerObservations(), _player); - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(itemDidPlayToEndTime:) - name:AVPlayerItemDidPlayToEndTimeNotification - object:item]; - [asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ] completionHandler:assetCompletionHandler]; return self; } - (void)dealloc { - if (!_disposed) { + if (_listenersRegistered && !_disposed) { // If dispose was never called for some reason, remove observers to prevent crashes. FVPRemoveKeyValueObservers(self, FVPGetPlayerItemObservations(), _player.currentItem); FVPRemoveKeyValueObservers(self, FVPGetPlayerObservations(), _player); @@ -164,9 +159,11 @@ - (void)dispose { } _disposed = YES; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - FVPRemoveKeyValueObservers(self, FVPGetPlayerItemObservations(), self.player.currentItem); - FVPRemoveKeyValueObservers(self, FVPGetPlayerObservations(), self.player); + if (_listenersRegistered) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + FVPRemoveKeyValueObservers(self, FVPGetPlayerItemObservations(), self.player.currentItem); + FVPRemoveKeyValueObservers(self, FVPGetPlayerObservations(), self.player); + } [self.player replaceCurrentItemWithPlayerItem:nil]; @@ -176,6 +173,25 @@ - (void)dispose { [self.eventListener videoPlayerWasDisposed]; } +- (void)setEventListener:(NSObject *)eventListener { + _eventListener = eventListener; + // The first time an event listener is set, set up video event listeners to relay status changes + // changes to the event listener. + if (eventListener && !_listenersRegistered) { + AVPlayerItem *item = self.player.currentItem; + // If the item is already ready to play, ensure that the intialized event is sent first. + [self reportStatusForPlayerItem:item]; + // Set up all necessary observers to report video events. + FVPRegisterKeyValueObservers(self, FVPGetPlayerItemObservations(), item); + FVPRegisterKeyValueObservers(self, FVPGetPlayerObservations(), _player); + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(itemDidPlayToEndTime:) + name:AVPlayerItemDidPlayToEndTimeNotification + object:item]; + _listenersRegistered = YES; + } +} + - (void)itemDidPlayToEndTime:(NSNotification *)notification { if (_isLooping) { AVPlayerItem *p = [notification object]; @@ -260,17 +276,7 @@ - (void)observeValueForKeyPath:(NSString *)path [self.eventListener videoPlayerDidUpdateBufferRegions:values]; } else if (context == statusContext) { AVPlayerItem *item = (AVPlayerItem *)object; - switch (item.status) { - case AVPlayerItemStatusFailed: - [self sendFailedToLoadVideoEvent]; - break; - case AVPlayerItemStatusUnknown: - break; - case AVPlayerItemStatusReadyToPlay: - [item addOutput:_videoOutput]; - [self reportInitializedIfReadyToPlay]; - break; - } + [self reportStatusForPlayerItem:item]; } else if (context == presentationSizeContext || context == durationContext) { AVPlayerItem *item = (AVPlayerItem *)object; if (item.status == AVPlayerItemStatusReadyToPlay) { @@ -294,6 +300,20 @@ - (void)observeValueForKeyPath:(NSString *)path } } +- (void)reportStatusForPlayerItem:(AVPlayerItem *)item { + switch (item.status) { + case AVPlayerItemStatusFailed: + [self sendFailedToLoadVideoEvent]; + break; + case AVPlayerItemStatusUnknown: + break; + case AVPlayerItemStatusReadyToPlay: + [item addOutput:_videoOutput]; + [self reportInitializedIfReadyToPlay]; + break; + } +} + - (void)updatePlayingState { if (!_isInitialized) { return; From 588b33edbdbe8278a0d9443e1649860fe96f9b8c Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 5 Aug 2025 09:52:00 -0400 Subject: [PATCH 7/7] C warning fix --- .../Sources/video_player_avfoundation/FVPVideoPlayer.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 ffd3891d52c..4667441cb97 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 @@ -52,7 +52,7 @@ static void FVPRemoveKeyValueObservers(NSObject *observer, /// Returns a mapping of KVO keys to NSValue-wrapped observer context pointers for observations that /// should be set for AVPlayer instances. -static NSDictionary *FVPGetPlayerObservations() { +static NSDictionary *FVPGetPlayerObservations(void) { return @{ @"rate" : [NSValue valueWithPointer:rateContext], }; @@ -60,7 +60,7 @@ static void FVPRemoveKeyValueObservers(NSObject *observer, /// Returns a mapping of KVO keys to NSValue-wrapped observer context pointers for observations that /// should be set for AVPlayerItem instances. -static NSDictionary *FVPGetPlayerItemObservations() { +static NSDictionary *FVPGetPlayerItemObservations(void) { return @{ @"loadedTimeRanges" : [NSValue valueWithPointer:timeRangeContext], @"status" : [NSValue valueWithPointer:statusContext],