diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 982b1ae65fa..df6b448cfe3 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.4 + +* Simplifies native code. + ## 2.8.3 * Removes calls to self from init and dealloc, for maintainability. 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 9541226aeff..6e2afec3c96 100644 --- a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m @@ -243,13 +243,13 @@ - (void)testBlankVideoBugWithEncryptedVideoStreamAndInvertedAspectRatioBugForSom FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; + httpHeaders:@{}]; + FVPTexturePlayerIds *identifiers = [videoPlayerPlugin createTexturePlayerWithOptions:create + error:&error]; XCTAssertNil(error); - XCTAssertNotNil(playerIdentifier); + XCTAssertNotNil(identifiers); FVPTextureBasedVideoPlayer *player = - (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[@(identifiers.playerId)]; XCTAssertNotNil(player); XCTAssertNotNil(player.playerLayer, @"AVPlayerLayer should be present."); @@ -275,10 +275,9 @@ - (void)testPlayerForPlatformViewDoesNotRegisterTexture { XCTAssertNil(initializationError); FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypePlatformView]; + httpHeaders:@{}]; FlutterError *createError; - [videoPlayerPlugin createWithOptions:create error:&createError]; + [videoPlayerPlugin createPlatformViewPlayerWithOptions:create error:&createError]; OCMVerify(never(), [mockTextureRegistry registerTexture:[OCMArg any]]); } @@ -303,12 +302,12 @@ - (void)testSeekToWhilePausedStartsDisplayLinkTemporarily { XCTAssertNil(initializationError); FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + httpHeaders:@{}]; FlutterError *createError; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&createError]; + FVPTexturePlayerIds *identifiers = + [videoPlayerPlugin createTexturePlayerWithOptions:create error:&createError]; FVPTextureBasedVideoPlayer *player = - (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[@(identifiers.playerId)]; // Ensure that the video playback is paused before seeking. FlutterError *pauseError; @@ -360,10 +359,10 @@ - (void)testInitStartsDisplayLinkTemporarily { XCTAssertNil(initializationError); FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + httpHeaders:@{}]; FlutterError *createError; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&createError]; + FVPTexturePlayerIds *identifiers = + [videoPlayerPlugin createTexturePlayerWithOptions:create error:&createError]; // Init should start the display link temporarily. XCTAssertTrue(stubDisplayLinkFactory.displayLink.running); @@ -379,7 +378,7 @@ - (void)testInitStartsDisplayLinkTemporarily { .andReturn(bufferRef); // Simulate a callback from the engine to request a new frame. FVPTextureBasedVideoPlayer *player = - (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[@(identifiers.playerId)]; stubDisplayLinkFactory.fireDisplayLink(); CFRelease([player copyPixelBuffer]); // Since a frame was found, and the video is paused, the display link should be paused again. @@ -406,12 +405,12 @@ - (void)testSeekToWhilePlayingDoesNotStopDisplayLink { XCTAssertNil(initializationError); FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + httpHeaders:@{}]; FlutterError *createError; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&createError]; + FVPTexturePlayerIds *identifiers = + [videoPlayerPlugin createTexturePlayerWithOptions:create error:&createError]; FVPTextureBasedVideoPlayer *player = - (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[@(identifiers.playerId)]; // Ensure that the video is playing before seeking. FlutterError *playError; @@ -461,12 +460,12 @@ - (void)testPauseWhileWaitingForFrameDoesNotStopDisplayLink { XCTAssertNil(initializationError); FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + httpHeaders:@{}]; FlutterError *createError; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&createError]; + FVPTexturePlayerIds *identifiers = + [videoPlayerPlugin createTexturePlayerWithOptions:create error:&createError]; FVPTextureBasedVideoPlayer *player = - (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[@(identifiers.playerId)]; // Run a play/pause cycle to force the pause codepath to run completely. FlutterError *playPauseError; @@ -488,18 +487,18 @@ - (void)testDeregistersFromPlayer { FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; + httpHeaders:@{}]; + FVPTexturePlayerIds *identifiers = [videoPlayerPlugin createTexturePlayerWithOptions:create + error:&error]; XCTAssertNil(error); - XCTAssertNotNil(playerIdentifier); - FVPVideoPlayer *player = videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + XCTAssertNotNil(identifiers); + FVPVideoPlayer *player = videoPlayerPlugin.playersByIdentifier[@(identifiers.playerId)]; XCTAssertNotNil(player); AVPlayer *avPlayer = player.player; [self keyValueObservingExpectationForObject:avPlayer keyPath:@"currentItem" expectedValue:nil]; - [videoPlayerPlugin disposePlayer:playerIdentifier.integerValue error:&error]; + [player disposeWithError:&error]; XCTAssertEqual(videoPlayerPlugin.playersByIdentifier.count, 0); XCTAssertNil(error); @@ -517,12 +516,12 @@ - (void)testBufferingStateFromPlayer { FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; + httpHeaders:@{}]; + FVPTexturePlayerIds *identifiers = [videoPlayerPlugin createTexturePlayerWithOptions:create + error:&error]; XCTAssertNil(error); - XCTAssertNotNil(playerIdentifier); - FVPVideoPlayer *player = videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + XCTAssertNotNil(identifiers); + FVPVideoPlayer *player = videoPlayerPlugin.playersByIdentifier[@(identifiers.playerId)]; XCTAssertNotNil(player); AVPlayer *avPlayer = player.player; [avPlayer play]; @@ -604,10 +603,9 @@ - (void)testSeekToleranceWhenNotSeekingToEnd { StubFVPAVFactory *stubAVFactory = [[StubFVPAVFactory alloc] initWithPlayer:stubAVPlayer output:nil]; FVPVideoPlayer *player = - [[FVPVideoPlayer alloc] initWithURL:self.mp4TestURL - httpHeaders:@{} - avFactory:stubAVFactory - viewProvider:[[StubViewProvider alloc] initWithView:nil]]; + [[FVPVideoPlayer alloc] initWithPlayerItem:[self playerItemWithURL:self.mp4TestURL] + avFactory:stubAVFactory + viewProvider:[[StubViewProvider alloc] initWithView:nil]]; NSObject *listener = OCMProtocolMock(@protocol(FVPVideoEventListener)); player.eventListener = listener; @@ -628,10 +626,9 @@ - (void)testSeekToleranceWhenSeekingToEnd { StubFVPAVFactory *stubAVFactory = [[StubFVPAVFactory alloc] initWithPlayer:stubAVPlayer output:nil]; FVPVideoPlayer *player = - [[FVPVideoPlayer alloc] initWithURL:self.mp4TestURL - httpHeaders:@{} - avFactory:stubAVFactory - viewProvider:[[StubViewProvider alloc] initWithView:nil]]; + [[FVPVideoPlayer alloc] initWithPlayerItem:[self playerItemWithURL:self.mp4TestURL] + avFactory:stubAVFactory + viewProvider:[[StubViewProvider alloc] initWithView:nil]]; NSObject *listener = OCMProtocolMock(@protocol(FVPVideoEventListener)); player.eventListener = listener; @@ -655,10 +652,9 @@ - (StubEventListener *)sanityTestURI:(NSString *)testURI { NSURL *testURL = [NSURL URLWithString:testURI]; XCTAssertNotNil(testURL); FVPVideoPlayer *player = - [[FVPVideoPlayer alloc] initWithURL:testURL - httpHeaders:@{} - avFactory:[[FVPDefaultAVFactory alloc] init] - viewProvider:[[StubViewProvider alloc] initWithView:nil]]; + [[FVPVideoPlayer alloc] initWithPlayerItem:[self playerItemWithURL:testURL] + avFactory:[[FVPDefaultAVFactory alloc] init] + viewProvider:[[StubViewProvider alloc] initWithView:nil]]; XCTAssertNotNil(player); XCTestExpectation *initializedExpectation = [self expectationWithDescription:@"initialized"]; @@ -711,18 +707,18 @@ - (void)testDoesNotCrashOnRateObservationAfterDisposal { FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; + httpHeaders:@{}]; + FVPTexturePlayerIds *identifiers = [videoPlayerPlugin createTexturePlayerWithOptions:create + error:&error]; XCTAssertNil(error); - XCTAssertNotNil(playerIdentifier); + XCTAssertNotNil(identifiers); - FVPVideoPlayer *player = videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + FVPVideoPlayer *player = videoPlayerPlugin.playersByIdentifier[@(identifiers.playerId)]; XCTAssertNotNil(player); weakPlayer = player; avPlayer = player.player; - [videoPlayerPlugin disposePlayer:playerIdentifier.integerValue error:&error]; + [player disposeWithError:&error]; XCTAssertNil(error); } @@ -762,14 +758,15 @@ - (void)testHotReloadDoesNotCrash { FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; + httpHeaders:@{}]; + FVPTexturePlayerIds *identifiers = [videoPlayerPlugin createTexturePlayerWithOptions:create + error:&error]; XCTAssertNil(error); - XCTAssertNotNil(playerIdentifier); + XCTAssertNotNil(identifiers); FVPTextureBasedVideoPlayer *player = - (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + (FVPTextureBasedVideoPlayer *) + videoPlayerPlugin.playersByIdentifier[@(identifiers.playerId)]; XCTAssertNotNil(player); weakPlayer = player; @@ -825,11 +822,10 @@ - (void)testFailedToLoadVideoEventShouldBeAlwaysSent { [videoPlayerPlugin initialize:&error]; - FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; - FVPVideoPlayer *player = videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"" httpHeaders:@{}]; + FVPTexturePlayerIds *identifiers = [videoPlayerPlugin createTexturePlayerWithOptions:create + error:&error]; + FVPVideoPlayer *player = videoPlayerPlugin.playersByIdentifier[@(identifiers.playerId)]; XCTAssertNotNil(player); [self keyValueObservingExpectationForObject:(id)player.player.currentItem @@ -851,11 +847,10 @@ - (void)testFailedToLoadVideoEventShouldBeAlwaysSent { } - (void)testUpdatePlayingStateShouldNotResetRate { - FVPVideoPlayer *player = - [[FVPVideoPlayer alloc] initWithURL:self.mp4TestURL - httpHeaders:@{} - avFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:nil] - viewProvider:[[StubViewProvider alloc] initWithView:nil]]; + FVPVideoPlayer *player = [[FVPVideoPlayer alloc] + initWithPlayerItem:[self playerItemWithURL:self.mp4TestURL] + avFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:nil] + viewProvider:[[StubViewProvider alloc] initWithView:nil]]; XCTestExpectation *initializedExpectation = [self expectationWithDescription:@"initialized"]; StubEventListener *listener = @@ -888,11 +883,13 @@ - (void)testPlayerShouldNotDropEverySecondFrame { XCTAssertNil(error); FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; + httpHeaders:@{}]; + FVPTexturePlayerIds *identifiers = [videoPlayerPlugin createTexturePlayerWithOptions:create + error:&error]; + NSInteger playerIdentifier = identifiers.playerId; + NSInteger textureIdentifier = identifiers.textureId; FVPTextureBasedVideoPlayer *player = - (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[playerIdentifier]; + (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[@(playerIdentifier)]; __block CMTime currentTime = kCMTimeZero; OCMStub([mockVideoOutput itemTimeForHostTime:0]) @@ -928,12 +925,12 @@ - (void)testPlayerShouldNotDropEverySecondFrame { }; advanceFrame(); - OCMExpect([mockTextureRegistry textureFrameAvailable:playerIdentifier.intValue]); + OCMExpect([mockTextureRegistry textureFrameAvailable:textureIdentifier]); stubDisplayLinkFactory.fireDisplayLink(); OCMVerifyAllWithDelay(mockTextureRegistry, 10); advanceFrame(); - OCMExpect([mockTextureRegistry textureFrameAvailable:playerIdentifier.intValue]); + OCMExpect([mockTextureRegistry textureFrameAvailable:textureIdentifier]); CFRelease([player copyPixelBuffer]); stubDisplayLinkFactory.fireDisplayLink(); OCMVerifyAllWithDelay(mockTextureRegistry, 10); @@ -1023,4 +1020,8 @@ - (nonnull NSURL *)mp4TestURL { URLWithString:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4"]; } +- (nonnull AVPlayerItem *)playerItemWithURL:(NSURL *)url { + return [AVPlayerItem playerItemWithAsset:[AVURLAsset URLAssetWithURL:url options:nil]]; +} + @end 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 52e90a4fb42..664c9d28dc1 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 @@ -28,29 +28,13 @@ @interface FVPTextureBasedVideoPlayer () // (e.g., after a seek while paused). If YES, the display link should continue to run until the next // frame is successfully provided. @property(nonatomic, assign) BOOL waitingForFrame; + +/// Ensures that the frame updater runs until a frame is rendered, regardless of play/pause state. +- (void)expectFrame; @end @implementation FVPTextureBasedVideoPlayer -- (instancetype)initWithURL:(NSURL *)url - frameUpdater:(FVPFrameUpdater *)frameUpdater - displayLink:(NSObject *)displayLink - httpHeaders:(nonnull NSDictionary *)headers - avFactory:(id)avFactory - viewProvider:(NSObject *)viewProvider { - NSDictionary *options = nil; - if ([headers count] != 0) { - options = @{@"AVURLAssetHTTPHeaderFieldsKey" : headers}; - } - AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:options]; - AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:urlAsset]; - return [self initWithPlayerItem:item - frameUpdater:frameUpdater - displayLink:displayLink - avFactory:avFactory - viewProvider:viewProvider]; -} - - (instancetype)initWithPlayerItem:(AVPlayerItem *)item frameUpdater:(FVPFrameUpdater *)frameUpdater displayLink:(NSObject *)displayLink @@ -81,6 +65,10 @@ - (void)dealloc { - (void)setTextureIdentifier:(int64_t)textureIdentifier { self.frameUpdater.textureIdentifier = textureIdentifier; + + // Ensure that the first frame is drawn once available, even if the video isn't played, since + // the engine is now expecting the texture to be populated. + [self expectFrame]; } - (void)expectFrame { @@ -118,8 +106,8 @@ - (void)seekTo:(NSInteger)position completion:(void (^)(FlutterError *_Nullable) }]; } -- (void)dispose { - [super dispose]; +- (void)disposeWithError:(FlutterError *_Nullable *_Nonnull)error { + [super disposeWithError:error]; [self.playerLayer removeFromSuperlayer]; @@ -214,7 +202,8 @@ - (CVPixelBufferRef)copyPixelBuffer { - (void)onTextureUnregistered:(NSObject *)texture { dispatch_async(dispatch_get_main_queue(), ^{ if (!self.disposed) { - [self dispose]; + FlutterError *error; + [self disposeWithError:&error]; } }); } 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 4667441cb97..84d2ba9b32c 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 @@ -75,19 +75,6 @@ @implementation FVPVideoPlayer { BOOL _listenersRegistered; } -- (instancetype)initWithURL:(NSURL *)url - httpHeaders:(nonnull NSDictionary *)headers - avFactory:(id)avFactory - viewProvider:(NSObject *)viewProvider { - NSDictionary *options = nil; - if ([headers count] != 0) { - options = @{@"AVURLAssetHTTPHeaderFieldsKey" : headers}; - } - AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:options]; - AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:urlAsset]; - return [self initWithPlayerItem:item avFactory:avFactory viewProvider:viewProvider]; -} - - (instancetype)initWithPlayerItem:(AVPlayerItem *)item avFactory:(id)avFactory viewProvider:(NSObject *)viewProvider { @@ -152,7 +139,7 @@ - (void)dealloc { } } -- (void)dispose { +- (void)disposeWithError:(FlutterError *_Nullable *_Nonnull)error { // In some hot restart scenarios, dispose can be called twice, so no-op after the first time. if (_disposed) { return; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m index aa9d6f74b2b..d87b70699bd 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m @@ -18,10 +18,6 @@ // https://github.com/flutter/packages/pull/6675/#discussion_r1591210702 #import "./include/video_player_avfoundation/messages.g.h" -#if !__has_feature(objc_arc) -#error Code Requires ARC. -#endif - /// Non-test implementation of the diplay link factory. @interface FVPDefaultDisplayLinkFactory : NSObject @end @@ -48,8 +44,7 @@ @interface FVPVideoPlayerPlugin () @property(nonatomic, strong) id displayLinkFactory; @property(nonatomic, strong) id avFactory; @property(nonatomic, strong) NSObject *viewProvider; -// TODO(stuartmorgan): Decouple identifiers for platform views and texture views. -@property(nonatomic, assign) int64_t nextNonTexturePlayerIdentifier; +@property(nonatomic, assign) int64_t nextPlayerIdentifier; @end @implementation FVPVideoPlayerPlugin @@ -84,49 +79,39 @@ - (instancetype)initWithAVFactory:(id)avFactory _avFactory = avFactory ?: [[FVPDefaultAVFactory alloc] init]; _viewProvider = viewProvider ?: [[FVPDefaultViewProvider alloc] initWithRegistrar:registrar]; _playersByIdentifier = [NSMutableDictionary dictionaryWithCapacity:1]; - // Initialized to a high number to avoid collisions with texture identifiers (which are generated - // separately). - _nextNonTexturePlayerIdentifier = INT_MAX; + _nextPlayerIdentifier = 1; return self; } - (void)detachFromEngineForRegistrar:(NSObject *)registrar { + FlutterError *error; for (FVPVideoPlayer *player in self.playersByIdentifier.allValues) { // Remove the channel and texture cleanup, and the event listener, to ensure that the player // doesn't message the engine that is no longer connected. player.onDisposed = nil; player.eventListener = nil; - [player dispose]; + [player disposeWithError:&error]; } [self.playersByIdentifier removeAllObjects]; SetUpFVPAVFoundationVideoPlayerApi(registrar.messenger, nil); } -- (int64_t)onPlayerSetup:(FVPVideoPlayer *)player { - FVPTextureBasedVideoPlayer *textureBasedPlayer = - [player isKindOfClass:[FVPTextureBasedVideoPlayer class]] - ? (FVPTextureBasedVideoPlayer *)player - : nil; - - int64_t playerIdentifier; - if (textureBasedPlayer) { - playerIdentifier = [self.registrar.textures registerTexture:textureBasedPlayer]; - [textureBasedPlayer setTextureIdentifier:playerIdentifier]; - } else { - playerIdentifier = self.nextNonTexturePlayerIdentifier--; - } +- (int64_t)configurePlayer:(FVPVideoPlayer *)player + withExtraDisposeHandler:(nullable void (^)(void))extraDisposeHandler { + int64_t playerIdentifier = self.nextPlayerIdentifier++; + self.playersByIdentifier[@(playerIdentifier)] = player; NSObject *messenger = self.registrar.messenger; NSString *channelSuffix = [NSString stringWithFormat:@"%lld", playerIdentifier]; // Set up the player-specific API handler, and its onDispose unregistration. SetUpFVPVideoPlayerInstanceApiWithSuffix(messenger, player, channelSuffix); __weak typeof(self) weakSelf = self; - BOOL isTextureBased = textureBasedPlayer != nil; player.onDisposed = ^() { SetUpFVPVideoPlayerInstanceApiWithSuffix(messenger, nil, channelSuffix); - if (isTextureBased) { - [weakSelf.registrar.textures unregisterTexture:playerIdentifier]; + if (extraDisposeHandler) { + extraDisposeHandler(); } + [weakSelf.playersByIdentifier removeObjectForKey:@(playerIdentifier)]; }; // Set up the event channel. FVPEventBridge *eventBridge = [[FVPEventBridge alloc] @@ -135,12 +120,6 @@ - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player { channelSuffix]]; player.eventListener = eventBridge; - self.playersByIdentifier[@(playerIdentifier)] = player; - - // Ensure that the first frame is drawn once available, even if the video isn't played, since - // the engine is now expecting the texture to be populated. - [textureBasedPlayer expectFrame]; - return playerIdentifier; } @@ -186,55 +165,64 @@ - (void)initialize:(FlutterError *__autoreleasing *)error { upgradeAudioSessionCategory(AVAudioSessionCategoryPlayback, 0, 0); #endif - [self.playersByIdentifier.allValues makeObjectsPerformSelector:@selector(dispose)]; + FlutterError *disposeError; + // Disposing a player removes it from the dictionary, so iterate over a copy. + NSArray *players = [self.playersByIdentifier.allValues copy]; + for (FVPVideoPlayer *player in players) { + [player disposeWithError:&disposeError]; + } [self.playersByIdentifier removeAllObjects]; } -- (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options - error:(FlutterError **)error { - BOOL textureBased = options.viewType == FVPPlatformVideoViewTypeTextureView; - +- (nullable NSNumber *)createPlatformViewPlayerWithOptions:(nonnull FVPCreationOptions *)options + error:(FlutterError **)error { @try { - FVPVideoPlayer *player = textureBased ? [self texturePlayerWithOptions:options] - : [self platformViewPlayerWithOptions:options]; - return @([self onPlayerSetup:player]); + AVPlayerItem *item = [self playerItemWithCreationOptions:options]; + + // FVPVideoPlayer contains all required logic for platform views. + FVPVideoPlayer *player = [[FVPVideoPlayer alloc] initWithPlayerItem:item + avFactory:self.avFactory + viewProvider:self.viewProvider]; + + return @([self configurePlayer:player withExtraDisposeHandler:nil]); } @catch (NSException *exception) { *error = [FlutterError errorWithCode:@"video_player" message:exception.reason details:nil]; return nil; } } -- (nonnull FVPTextureBasedVideoPlayer *)texturePlayerWithOptions: - (nonnull FVPCreationOptions *)options { - FVPFrameUpdater *frameUpdater = - [[FVPFrameUpdater alloc] initWithRegistry:self.registrar.textures]; - NSObject *displayLink = - [self.displayLinkFactory displayLinkWithRegistrar:_registrar - callback:^() { - [frameUpdater displayLinkFired]; - }]; - - return [[FVPTextureBasedVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri] - frameUpdater:frameUpdater - displayLink:displayLink - httpHeaders:options.httpHeaders - avFactory:self.avFactory - viewProvider:self.viewProvider]; -} - -- (nonnull FVPVideoPlayer *)platformViewPlayerWithOptions:(nonnull FVPCreationOptions *)options { - // FVPVideoPlayer contains all required logic for platform views. - return [[FVPVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri] - httpHeaders:options.httpHeaders - avFactory:self.avFactory - viewProvider:self.viewProvider]; -} - -- (void)disposePlayer:(NSInteger)playerIdentifier error:(FlutterError **)error { - NSNumber *playerKey = @(playerIdentifier); - FVPVideoPlayer *player = self.playersByIdentifier[playerKey]; - [self.playersByIdentifier removeObjectForKey:playerKey]; - [player dispose]; +- (nullable FVPTexturePlayerIds *)createTexturePlayerWithOptions: + (nonnull FVPCreationOptions *)options + error:(FlutterError **)error { + @try { + AVPlayerItem *item = [self playerItemWithCreationOptions:options]; + FVPFrameUpdater *frameUpdater = + [[FVPFrameUpdater alloc] initWithRegistry:self.registrar.textures]; + NSObject *displayLink = + [self.displayLinkFactory displayLinkWithRegistrar:_registrar + callback:^() { + [frameUpdater displayLinkFired]; + }]; + + FVPTextureBasedVideoPlayer *player = + [[FVPTextureBasedVideoPlayer alloc] initWithPlayerItem:item + frameUpdater:frameUpdater + displayLink:displayLink + avFactory:self.avFactory + viewProvider:self.viewProvider]; + + int64_t textureIdentifier = [self.registrar.textures registerTexture:player]; + [player setTextureIdentifier:textureIdentifier]; + __weak typeof(self) weakSelf = self; + int64_t playerIdentifier = [self configurePlayer:player + withExtraDisposeHandler:^() { + [weakSelf.registrar.textures unregisterTexture:textureIdentifier]; + }]; + return [FVPTexturePlayerIds makeWithPlayerId:playerIdentifier textureId:textureIdentifier]; + } @catch (NSException *exception) { + *error = [FlutterError errorWithCode:@"video_player" message:exception.reason details:nil]; + return nil; + } } - (void)setMixWithOthers:(BOOL)mixWithOthers @@ -274,4 +262,14 @@ - (nullable NSString *)fileURLForAssetWithName:(NSString *)asset return [NSURL fileURLWithPath:path].absoluteString; } +/// Returns the AVPlayerItem corresponding to the given player creation options. +- (nonnull AVPlayerItem *)playerItemWithCreationOptions:(nonnull FVPCreationOptions *)options { + NSDictionary *headers = options.httpHeaders; + NSDictionary *itemOptions = + headers.count == 0 ? nil : @{@"AVURLAssetHTTPHeaderFieldsKey" : headers}; + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:options.uri] + options:itemOptions]; + return [AVPlayerItem playerItemWithAsset:asset]; +} + @end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPTextureBasedVideoPlayer.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPTextureBasedVideoPlayer.h index bae9105039e..6b02e5449e7 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPTextureBasedVideoPlayer.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPTextureBasedVideoPlayer.h @@ -14,22 +14,17 @@ NS_ASSUME_NONNULL_BEGIN /// updates frames, and handles display link callbacks. /// If you need to display a video using platform view, use FVPVideoPlayer instead. @interface FVPTextureBasedVideoPlayer : FVPVideoPlayer -/// Initializes a new instance of FVPTextureBasedVideoPlayer with the given URL, frame updater, -/// display link, HTTP headers, AV factory, and registrar. -- (instancetype)initWithURL:(NSURL *)url - frameUpdater:(FVPFrameUpdater *)frameUpdater - displayLink:(NSObject *)displayLink - httpHeaders:(nonnull NSDictionary *)headers - avFactory:(id)avFactory - viewProvider:(NSObject *)viewProvider; +/// Initializes a new instance of FVPTextureBasedVideoPlayer with the given player item, +/// frame updater, display link, AV factory, and view provider. +- (instancetype)initWithPlayerItem:(AVPlayerItem *)item + frameUpdater:(FVPFrameUpdater *)frameUpdater + displayLink:(NSObject *)displayLink + avFactory:(id)avFactory + viewProvider:(NSObject *)viewProvider; /// Sets the texture Identifier for the frame updater. This method should be called once the texture /// identifier is obtained from the texture registry. - (void)setTextureIdentifier:(int64_t)textureIdentifier; - -/// Tells the player to run its frame updater until it receives a frame, regardless of the -/// play/pause state. -- (void)expectFrame; @end NS_ASSUME_NONNULL_END diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h index 52705c1c9b5..affee88a96e 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h @@ -36,15 +36,11 @@ NS_ASSUME_NONNULL_BEGIN /// A block that will be called when dispose is called. @property(nonatomic, nullable, copy) void (^onDisposed)(void); -/// Initializes a new instance of FVPVideoPlayer with the given URL, HTTP headers, AV factory, and -/// view provider. -- (instancetype)initWithURL:(NSURL *)url - httpHeaders:(nonnull NSDictionary *)headers - avFactory:(id)avFactory - viewProvider:(NSObject *)viewProvider; - -/// Disposes the video player and releases any resources it holds. -- (void)dispose; +/// Initializes a new instance of FVPVideoPlayer with the given AVPlayerItem, AV factory, and view +/// provider. +- (instancetype)initWithPlayerItem:(AVPlayerItem *)item + avFactory:(id)avFactory + viewProvider:(NSObject *)viewProvider; @end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer_Internal.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer_Internal.h index dedfce6ef63..9a522591207 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer_Internal.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer_Internal.h @@ -25,12 +25,6 @@ NS_ASSUME_NONNULL_BEGIN /// Indicates whether the video player has been initialized. @property(nonatomic, readonly) BOOL isInitialized; -/// Initializes a new instance of FVPVideoPlayer with the given AVPlayerItem, frame updater, display -/// link, AV factory, and view provider. -- (instancetype)initWithPlayerItem:(AVPlayerItem *)item - avFactory:(id)avFactory - viewProvider:(NSObject *)viewProvider; - /// Updates the playing state of the video player. - (void)updatePlayingState; @end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h index b895043931e..311e25dbab4 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h @@ -13,20 +13,9 @@ NS_ASSUME_NONNULL_BEGIN -/// Pigeon equivalent of VideoViewType. -typedef NS_ENUM(NSUInteger, FVPPlatformVideoViewType) { - FVPPlatformVideoViewTypeTextureView = 0, - FVPPlatformVideoViewTypePlatformView = 1, -}; - -/// Wrapper for FVPPlatformVideoViewType to allow for nullability. -@interface FVPPlatformVideoViewTypeBox : NSObject -@property(nonatomic, assign) FVPPlatformVideoViewType value; -- (instancetype)initWithValue:(FVPPlatformVideoViewType)value; -@end - @class FVPPlatformVideoViewCreationParams; @class FVPCreationOptions; +@class FVPTexturePlayerIds; /// Information passed to the platform view creation. @interface FVPPlatformVideoViewCreationParams : NSObject @@ -40,11 +29,17 @@ typedef NS_ENUM(NSUInteger, FVPPlatformVideoViewType) { /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithUri:(NSString *)uri - httpHeaders:(NSDictionary *)httpHeaders - viewType:(FVPPlatformVideoViewType)viewType; + httpHeaders:(NSDictionary *)httpHeaders; @property(nonatomic, copy) NSString *uri; @property(nonatomic, copy) NSDictionary *httpHeaders; -@property(nonatomic, assign) FVPPlatformVideoViewType viewType; +@end + +@interface FVPTexturePlayerIds : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithPlayerId:(NSInteger)playerId textureId:(NSInteger)textureId; +@property(nonatomic, assign) NSInteger playerId; +@property(nonatomic, assign) NSInteger textureId; @end /// The codec used by all APIs. @@ -53,9 +48,12 @@ NSObject *FVPGetMessagesCodec(void); @protocol FVPAVFoundationVideoPlayerApi - (void)initialize:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSNumber *)createWithOptions:(FVPCreationOptions *)creationOptions - error:(FlutterError *_Nullable *_Nonnull)error; -- (void)disposePlayer:(NSInteger)playerId error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSNumber *)createPlatformViewPlayerWithOptions:(FVPCreationOptions *)params + error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable FVPTexturePlayerIds *) + createTexturePlayerWithOptions:(FVPCreationOptions *)creationOptions + error:(FlutterError *_Nullable *_Nonnull)error; - (void)setMixWithOthers:(BOOL)mixWithOthers error:(FlutterError *_Nullable *_Nonnull)error; - (nullable NSString *)fileURLForAssetWithName:(NSString *)asset package:(nullable NSString *)package @@ -79,6 +77,7 @@ extern void SetUpFVPAVFoundationVideoPlayerApiWithSuffix( - (nullable NSNumber *)position:(FlutterError *_Nullable *_Nonnull)error; - (void)seekTo:(NSInteger)position completion:(void (^)(FlutterError *_Nullable))completion; - (void)pauseWithError:(FlutterError *_Nullable *_Nonnull)error; +- (void)disposeWithError:(FlutterError *_Nullable *_Nonnull)error; @end extern void SetUpFVPVideoPlayerInstanceApi(id binaryMessenger, diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m index 13076be0e91..172807b1347 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m @@ -30,17 +30,6 @@ static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { return (result == [NSNull null]) ? nil : result; } -/// Pigeon equivalent of VideoViewType. -@implementation FVPPlatformVideoViewTypeBox -- (instancetype)initWithValue:(FVPPlatformVideoViewType)value { - self = [super init]; - if (self) { - _value = value; - } - return self; -} -@end - @interface FVPPlatformVideoViewCreationParams () + (FVPPlatformVideoViewCreationParams *)fromList:(NSArray *)list; + (nullable FVPPlatformVideoViewCreationParams *)nullableFromList:(NSArray *)list; @@ -53,6 +42,12 @@ + (nullable FVPCreationOptions *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FVPTexturePlayerIds () ++ (FVPTexturePlayerIds *)fromList:(NSArray *)list; ++ (nullable FVPTexturePlayerIds *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @implementation FVPPlatformVideoViewCreationParams + (instancetype)makeWithPlayerId:(NSInteger)playerId { FVPPlatformVideoViewCreationParams *pigeonResult = @@ -78,20 +73,16 @@ + (nullable FVPPlatformVideoViewCreationParams *)nullableFromList:(NSArray * @implementation FVPCreationOptions + (instancetype)makeWithUri:(NSString *)uri - httpHeaders:(NSDictionary *)httpHeaders - viewType:(FVPPlatformVideoViewType)viewType { + httpHeaders:(NSDictionary *)httpHeaders { FVPCreationOptions *pigeonResult = [[FVPCreationOptions alloc] init]; pigeonResult.uri = uri; pigeonResult.httpHeaders = httpHeaders; - pigeonResult.viewType = viewType; return pigeonResult; } + (FVPCreationOptions *)fromList:(NSArray *)list { FVPCreationOptions *pigeonResult = [[FVPCreationOptions alloc] init]; pigeonResult.uri = GetNullableObjectAtIndex(list, 0); pigeonResult.httpHeaders = GetNullableObjectAtIndex(list, 1); - FVPPlatformVideoViewTypeBox *boxedFVPPlatformVideoViewType = GetNullableObjectAtIndex(list, 2); - pigeonResult.viewType = boxedFVPPlatformVideoViewType.value; return pigeonResult; } + (nullable FVPCreationOptions *)nullableFromList:(NSArray *)list { @@ -101,7 +92,30 @@ + (nullable FVPCreationOptions *)nullableFromList:(NSArray *)list { return @[ self.uri ?: [NSNull null], self.httpHeaders ?: [NSNull null], - [[FVPPlatformVideoViewTypeBox alloc] initWithValue:self.viewType], + ]; +} +@end + +@implementation FVPTexturePlayerIds ++ (instancetype)makeWithPlayerId:(NSInteger)playerId textureId:(NSInteger)textureId { + FVPTexturePlayerIds *pigeonResult = [[FVPTexturePlayerIds alloc] init]; + pigeonResult.playerId = playerId; + pigeonResult.textureId = textureId; + return pigeonResult; +} ++ (FVPTexturePlayerIds *)fromList:(NSArray *)list { + FVPTexturePlayerIds *pigeonResult = [[FVPTexturePlayerIds alloc] init]; + pigeonResult.playerId = [GetNullableObjectAtIndex(list, 0) integerValue]; + pigeonResult.textureId = [GetNullableObjectAtIndex(list, 1) integerValue]; + return pigeonResult; +} ++ (nullable FVPTexturePlayerIds *)nullableFromList:(NSArray *)list { + return (list) ? [FVPTexturePlayerIds fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.playerId), + @(self.textureId), ]; } @end @@ -111,16 +125,12 @@ @interface FVPMessagesPigeonCodecReader : FlutterStandardReader @implementation FVPMessagesPigeonCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { - case 129: { - NSNumber *enumAsNumber = [self readValue]; - return enumAsNumber == nil - ? nil - : [[FVPPlatformVideoViewTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; - } - case 130: + case 129: return [FVPPlatformVideoViewCreationParams fromList:[self readValue]]; - case 131: + case 130: return [FVPCreationOptions fromList:[self readValue]]; + case 131: + return [FVPTexturePlayerIds fromList:[self readValue]]; default: return [super readValueOfType:type]; } @@ -131,14 +141,13 @@ @interface FVPMessagesPigeonCodecWriter : FlutterStandardWriter @end @implementation FVPMessagesPigeonCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[FVPPlatformVideoViewTypeBox class]]) { - FVPPlatformVideoViewTypeBox *box = (FVPPlatformVideoViewTypeBox *)value; + if ([value isKindOfClass:[FVPPlatformVideoViewCreationParams class]]) { [self writeByte:129]; - [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; - } else if ([value isKindOfClass:[FVPPlatformVideoViewCreationParams class]]) { - [self writeByte:130]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FVPCreationOptions class]]) { + [self writeByte:130]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FVPTexturePlayerIds class]]) { [self writeByte:131]; [self writeValue:[value toList]]; } else { @@ -202,22 +211,23 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.create", - messageChannelSuffix] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.createForPlatformView", + messageChannelSuffix] binaryMessenger:binaryMessenger codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(createWithOptions:error:)], + NSCAssert([api respondsToSelector:@selector(createPlatformViewPlayerWithOptions:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(createWithOptions:error:)", + @"@selector(createPlatformViewPlayerWithOptions:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FVPCreationOptions *arg_creationOptions = GetNullableObjectAtIndex(args, 0); + FVPCreationOptions *arg_params = GetNullableObjectAtIndex(args, 0); FlutterError *error; - NSNumber *output = [api createWithOptions:arg_creationOptions error:&error]; + NSNumber *output = [api createPlatformViewPlayerWithOptions:arg_params error:&error]; callback(wrapResult(output, error)); }]; } else { @@ -226,23 +236,25 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.dispose", - messageChannelSuffix] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.createForTextureView", + messageChannelSuffix] binaryMessenger:binaryMessenger codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(disposePlayer:error:)], + NSCAssert([api respondsToSelector:@selector(createTexturePlayerWithOptions:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(disposePlayer:error:)", + @"@selector(createTexturePlayerWithOptions:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSInteger arg_playerId = [GetNullableObjectAtIndex(args, 0) integerValue]; + FVPCreationOptions *arg_creationOptions = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api disposePlayer:arg_playerId error:&error]; - callback(wrapResult(nil, error)); + FVPTexturePlayerIds *output = [api createTexturePlayerWithOptions:arg_creationOptions + error:&error]; + callback(wrapResult(output, error)); }]; } else { [channel setMessageHandler:nil]; @@ -468,4 +480,26 @@ void SetUpFVPVideoPlayerInstanceApiWithSuffix(id binaryM [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"VideoPlayerInstanceApi.dispose", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FVPGetMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(disposeWithError:)], + @"FVPVideoPlayerInstanceApi api (%@) doesn't respond to @selector(disposeWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + [api disposeWithError:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } } diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index 3558dccd67c..aaccb8a2eb8 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -52,9 +52,9 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Future dispose(int playerId) async { - await _api.dispose(playerId); + final VideoPlayerInstanceApi? player = _players.remove(playerId); + await player?.dispose(); playerViewStates.remove(playerId); - _players.remove(playerId); } @override @@ -106,16 +106,21 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { final CreationOptions pigeonCreationOptions = CreationOptions( uri: uri, httpHeaders: dataSource.httpHeaders, - viewType: _platformVideoViewTypeFromVideoViewType(viewType), ); - final int playerId = await _api.create(pigeonCreationOptions); - playerViewStates[playerId] = switch (viewType) { - // playerId is also the textureId when using texture view. - VideoViewType.textureView => - VideoPlayerTextureViewState(textureId: playerId), - VideoViewType.platformView => const VideoPlayerPlatformViewState(), - }; + final int playerId; + final VideoPlayerViewState state; + switch (viewType) { + case VideoViewType.textureView: + final TexturePlayerIds ids = + await _api.createForTextureView(pigeonCreationOptions); + playerId = ids.playerId; + state = VideoPlayerTextureViewState(textureId: ids.textureId); + case VideoViewType.platformView: + playerId = await _api.createForPlatformView(pigeonCreationOptions); + state = const VideoPlayerPlatformViewState(); + } + playerViewStates[playerId] = state; ensureApiInitialized(playerId); return playerId; @@ -265,15 +270,6 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { } } -PlatformVideoViewType _platformVideoViewTypeFromVideoViewType( - VideoViewType viewType, -) { - return switch (viewType) { - VideoViewType.textureView => PlatformVideoViewType.textureView, - VideoViewType.platformView => PlatformVideoViewType.platformView, - }; -} - /// Base class representing the state of a video player view. @visibleForTesting @immutable diff --git a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart index ae5ec1d9f6c..8cc8bf343ba 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart @@ -33,12 +33,6 @@ bool _deepEquals(Object? a, Object? b) { return a == b; } -/// Pigeon equivalent of VideoViewType. -enum PlatformVideoViewType { - textureView, - platformView, -} - /// Information passed to the platform view creation. class PlatformVideoViewCreationParams { PlatformVideoViewCreationParams({ @@ -86,20 +80,16 @@ class CreationOptions { CreationOptions({ required this.uri, required this.httpHeaders, - required this.viewType, }); String uri; Map httpHeaders; - PlatformVideoViewType viewType; - List _toList() { return [ uri, httpHeaders, - viewType, ]; } @@ -113,7 +103,6 @@ class CreationOptions { uri: result[0]! as String, httpHeaders: (result[1] as Map?)!.cast(), - viewType: result[2]! as PlatformVideoViewType, ); } @@ -134,6 +123,52 @@ class CreationOptions { int get hashCode => Object.hashAll(_toList()); } +class TexturePlayerIds { + TexturePlayerIds({ + required this.playerId, + required this.textureId, + }); + + int playerId; + + int textureId; + + List _toList() { + return [ + playerId, + textureId, + ]; + } + + Object encode() { + return _toList(); + } + + static TexturePlayerIds decode(Object result) { + result as List; + return TexturePlayerIds( + playerId: result[0]! as int, + textureId: result[1]! as int, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! TexturePlayerIds || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -141,13 +176,13 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is PlatformVideoViewType) { - buffer.putUint8(129); - writeValue(buffer, value.index); } else if (value is PlatformVideoViewCreationParams) { - buffer.putUint8(130); + buffer.putUint8(129); writeValue(buffer, value.encode()); } else if (value is CreationOptions) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is TexturePlayerIds) { buffer.putUint8(131); writeValue(buffer, value.encode()); } else { @@ -159,12 +194,11 @@ class _PigeonCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 129: - final int? value = readValue(buffer) as int?; - return value == null ? null : PlatformVideoViewType.values[value]; - case 130: return PlatformVideoViewCreationParams.decode(readValue(buffer)!); - case 131: + case 130: return CreationOptions.decode(readValue(buffer)!); + case 131: + return TexturePlayerIds.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -211,9 +245,9 @@ class AVFoundationVideoPlayerApi { } } - Future create(CreationOptions creationOptions) async { + Future createForPlatformView(CreationOptions params) async { final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.createForPlatformView$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, @@ -221,7 +255,7 @@ class AVFoundationVideoPlayerApi { binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = - pigeonVar_channel.send([creationOptions]); + pigeonVar_channel.send([params]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -242,9 +276,10 @@ class AVFoundationVideoPlayerApi { } } - Future dispose(int playerId) async { + Future createForTextureView( + CreationOptions creationOptions) async { final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.createForTextureView$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, @@ -252,7 +287,7 @@ class AVFoundationVideoPlayerApi { binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = - pigeonVar_channel.send([playerId]); + pigeonVar_channel.send([creationOptions]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -263,8 +298,13 @@ class AVFoundationVideoPlayerApi { message: pigeonVar_replyList[1] as String?, details: pigeonVar_replyList[2], ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { - return; + return (pigeonVar_replyList[0] as TexturePlayerIds?)!; } } @@ -519,4 +559,29 @@ class VideoPlayerInstanceApi { return; } } + + Future dispose() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.VideoPlayerInstanceApi.dispose$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } } diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart index 4b336cca5e6..550a282dbb0 100644 --- a/packages/video_player/video_player_avfoundation/pigeons/messages.dart +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -17,12 +17,6 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', )) -/// Pigeon equivalent of VideoViewType. -enum PlatformVideoViewType { - textureView, - platformView, -} - /// Information passed to the platform view creation. class PlatformVideoViewCreationParams { const PlatformVideoViewCreationParams({ @@ -36,23 +30,30 @@ class CreationOptions { CreationOptions({ required this.uri, required this.httpHeaders, - required this.viewType, }); String uri; Map httpHeaders; - PlatformVideoViewType viewType; +} + +class TexturePlayerIds { + TexturePlayerIds({required this.playerId, required this.textureId}); + + final int playerId; + final int textureId; } @HostApi() abstract class AVFoundationVideoPlayerApi { @ObjCSelector('initialize') void initialize(); - @ObjCSelector('createWithOptions:') - // Creates a new player and returns its ID. - int create(CreationOptions creationOptions); - @ObjCSelector('disposePlayer:') - void dispose(int playerId); + // Creates a new player using a platform view for rendering and returns its + // ID. + @ObjCSelector('createPlatformViewPlayerWithOptions:') + int createForPlatformView(CreationOptions params); + // Creates a new player using a texture for rendering and returns its IDs. + @ObjCSelector('createTexturePlayerWithOptions:') + TexturePlayerIds createForTextureView(CreationOptions creationOptions); @ObjCSelector('setMixWithOthers:') void setMixWithOthers(bool mixWithOthers); @ObjCSelector('fileURLForAssetWithName:package:') @@ -74,4 +75,5 @@ abstract class VideoPlayerInstanceApi { @ObjCSelector('seekTo:') void seekTo(int position); void pause(); + void dispose(); } diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index a9d17a56b67..cdc4f9cc814 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.3 +version: 2.8.4 environment: sdk: ^3.6.0 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 fb367310ec1..9aa6c8f2f43 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 @@ -55,12 +55,12 @@ void main() { test('dispose', () async { final ( AVFoundationVideoPlayer player, - MockAVFoundationVideoPlayerApi api, _, + MockVideoPlayerInstanceApi playerApi, ) = setUpMockPlayer(playerId: 1); await player.dispose(1); - verify(api.dispose(1)); + verify(playerApi.dispose()); expect(player.playerViewStates, isEmpty); }); @@ -71,7 +71,9 @@ void main() { _, ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + const int textureId = 100; + when(api.createForTextureView(any)).thenAnswer((_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); const String asset = 'someAsset'; const String package = 'somePackage'; @@ -88,13 +90,14 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); + final VerificationResult verification = + verify(api.createForTextureView(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, assetUrl); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: newPlayerId)); + const VideoPlayerTextureViewState(textureId: textureId)); }); test('create with asset throws PlatformException for missing asset', @@ -129,7 +132,9 @@ void main() { _, ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + const int textureId = 100; + when(api.createForTextureView(any)).thenAnswer((_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); const String uri = 'https://example.com'; final int? playerId = await player.create( @@ -140,14 +145,15 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); + final VerificationResult verification = + verify(api.createForTextureView(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, uri); expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: newPlayerId)); + const VideoPlayerTextureViewState(textureId: textureId)); }); test('create with network passes headers', () async { @@ -156,7 +162,8 @@ void main() { MockAVFoundationVideoPlayerApi api, _, ) = setUpMockPlayer(playerId: 1); - when(api.create(any)).thenAnswer((_) async => 2); + when(api.createForTextureView(any)).thenAnswer( + (_) async => TexturePlayerIds(playerId: 2, textureId: 100)); const Map headers = { 'Authorization': 'Bearer token', @@ -168,7 +175,8 @@ void main() { httpHeaders: headers, ), ); - final VerificationResult verification = verify(api.create(captureAny)); + final VerificationResult verification = + verify(api.createForTextureView(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.httpHeaders, headers); @@ -181,19 +189,22 @@ void main() { _, ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + const int textureId = 100; + when(api.createForTextureView(any)).thenAnswer((_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); const String fileUri = 'file:///foo/bar'; final int? playerId = await player.create( DataSource(sourceType: DataSourceType.file, uri: fileUri), ); - final VerificationResult verification = verify(api.create(captureAny)); + final VerificationResult verification = + verify(api.createForTextureView(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, fileUri); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: newPlayerId)); + const VideoPlayerTextureViewState(textureId: textureId)); }); test('createWithOptions with asset', () async { @@ -203,7 +214,9 @@ void main() { _, ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + const int textureId = 100; + when(api.createForTextureView(any)).thenAnswer((_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); const String asset = 'someAsset'; const String package = 'somePackage'; @@ -222,13 +235,14 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); + final VerificationResult verification = + verify(api.createForTextureView(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, assetUrl); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: newPlayerId)); + const VideoPlayerTextureViewState(textureId: textureId)); }); test('createWithOptions with network', () async { @@ -238,7 +252,9 @@ void main() { _, ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + const int textureId = 100; + when(api.createForTextureView(any)).thenAnswer((_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); const String uri = 'https://example.com'; final int? playerId = await player.createWithOptions( @@ -252,14 +268,15 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); + final VerificationResult verification = + verify(api.createForTextureView(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, uri); expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: newPlayerId)); + const VideoPlayerTextureViewState(textureId: textureId)); }); test('createWithOptions with network passes headers', () async { @@ -269,7 +286,8 @@ void main() { _, ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + when(api.createForTextureView(any)).thenAnswer( + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 100)); const Map headers = { 'Authorization': 'Bearer token', @@ -285,7 +303,8 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); + final VerificationResult verification = + verify(api.createForTextureView(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.httpHeaders, headers); @@ -299,7 +318,9 @@ void main() { _, ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + const int textureId = 100; + when(api.createForTextureView(any)).thenAnswer((_) async => + TexturePlayerIds(playerId: newPlayerId, textureId: textureId)); const String fileUri = 'file:///foo/bar'; final int? playerId = await player.createWithOptions( @@ -309,13 +330,14 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); + final VerificationResult verification = + verify(api.createForTextureView(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; expect(creationOptions.uri, fileUri); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], - const VideoPlayerTextureViewState(textureId: newPlayerId)); + const VideoPlayerTextureViewState(textureId: textureId)); }); test('createWithOptions with platform view', () async { @@ -325,7 +347,7 @@ void main() { _, ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + when(api.createForPlatformView(any)).thenAnswer((_) async => newPlayerId); final int? playerId = await player.createWithOptions( VideoCreationOptions( @@ -337,10 +359,6 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreationOptions creationOptions = - verification.captured[0] as CreationOptions; - expect(creationOptions.viewType, PlatformVideoViewType.platformView); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], const VideoPlayerPlatformViewState()); diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart index 76199b56c02..dfa4a76d4ae 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart @@ -23,6 +23,17 @@ import 'package:video_player_avfoundation/src/messages.g.dart' as _i2; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +class _FakeTexturePlayerIds_0 extends _i1.SmartFake + implements _i2.TexturePlayerIds { + _FakeTexturePlayerIds_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [AVFoundationVideoPlayerApi]. /// /// See the documentation for Mockito's code generation for more information. @@ -52,25 +63,41 @@ class MockAVFoundationVideoPlayerApi extends _i1.Mock ) as _i4.Future); @override - _i4.Future create(_i2.CreationOptions? creationOptions) => + _i4.Future createForPlatformView(_i2.CreationOptions? params) => (super.noSuchMethod( Invocation.method( - #create, - [creationOptions], + #createForPlatformView, + [params], ), returnValue: _i4.Future.value(0), returnValueForMissingStub: _i4.Future.value(0), ) as _i4.Future); @override - _i4.Future dispose(int? playerId) => (super.noSuchMethod( + _i4.Future<_i2.TexturePlayerIds> createForTextureView( + _i2.CreationOptions? creationOptions) => + (super.noSuchMethod( Invocation.method( - #dispose, - [playerId], + #createForTextureView, + [creationOptions], ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + returnValue: + _i4.Future<_i2.TexturePlayerIds>.value(_FakeTexturePlayerIds_0( + this, + Invocation.method( + #createForTextureView, + [creationOptions], + ), + )), + returnValueForMissingStub: + _i4.Future<_i2.TexturePlayerIds>.value(_FakeTexturePlayerIds_0( + this, + Invocation.method( + #createForTextureView, + [creationOptions], + ), + )), + ) as _i4.Future<_i2.TexturePlayerIds>); @override _i4.Future setMixWithOthers(bool? mixWithOthers) => (super.noSuchMethod( @@ -187,4 +214,14 @@ class MockVideoPlayerInstanceApi extends _i1.Mock returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + + @override + _i4.Future dispose() => (super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); }