-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[video_player] Simplify native iOS code #9800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
436fe4b
b3237fa
ecdaf2b
e1751bb
a88e47d
aada448
1aa6cd4
5604a09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,7 @@ | ||
| ## 2.8.4 | ||
|
|
||
| * Simplifies native code. | ||
|
|
||
| ## 2.8.3 | ||
|
|
||
| * Removes calls to self from init and dealloc, for maintainability. | ||
|
|
||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 <FVPDisplayLinkFactory> | ||
| @end | ||
|
|
@@ -48,8 +44,7 @@ @interface FVPVideoPlayerPlugin () | |
| @property(nonatomic, strong) id<FVPDisplayLinkFactory> displayLinkFactory; | ||
| @property(nonatomic, strong) id<FVPAVFactory> avFactory; | ||
| @property(nonatomic, strong) NSObject<FVPViewProvider> *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<FVPAVFactory>)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<FlutterPluginRegistrar> *)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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Out of curiosity, is there a reason for generating
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Historically, there was only the texture ID, and that was used throughout the plugin as the identifier for the player instance. When the platform view variant was added recently, that broke down; we still needed an identifier for each player, but not all players had texture IDs. The PRs that added platform view support renamed all the platform interface parameters from |
||
| playerIdentifier = self.nextNonTexturePlayerIdentifier--; | ||
| } | ||
| - (int64_t)configurePlayer:(FVPVideoPlayer *)player | ||
| withExtraDisposeHandler:(nullable void (^)(void))extraDisposeHandler { | ||
| int64_t playerIdentifier = self.nextPlayerIdentifier++; | ||
| self.playersByIdentifier[@(playerIdentifier)] = player; | ||
|
|
||
| NSObject<FlutterBinaryMessenger> *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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This local variable seems write-only?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, Pigeon generates its methods with non-null error pointers since it should always be calling them with a value, so they don't accidentally get dropped. The alternative here is that I could make a public
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah thanks for the explanation. I'm onboard with keeping the local variable then. |
||
| // Disposing a player removes it from the dictionary, so iterate over a copy. | ||
| NSArray<FVPVideoPlayer *> *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<FVPDisplayLink> *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<FVPDisplayLink> *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<NSString *, NSString *> *headers = options.httpHeaders; | ||
| NSDictionary<NSString *, id> *itemOptions = | ||
| headers.count == 0 ? nil : @{@"AVURLAssetHTTPHeaderFieldsKey" : headers}; | ||
| AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:options.uri] | ||
| options:itemOptions]; | ||
| return [AVPlayerItem playerItemWithAsset:asset]; | ||
| } | ||
|
|
||
| @end | ||
Uh oh!
There was an error while loading. Please reload this page.