Skip to content

Commit fc47eb7

Browse files
[video_player] Darwin implementation cleanup (#6507)
Does some cleanup/improvements to the iOS/macOS implementation in preparation for future work: - Modernizes the Pigeon API. The current version dates from when methods could only take and return a single class object, so had wrapper classes for everything that are no longer needed. - Removes unnecessary branching for iOS and macOS by enabling the new frame checking logic for iOS. This was only there to avoid coupling iOS behavioral changes to enabling initial macOS support. - Removes an async workaround that says it's for a race that was fixed in the engine a long time ago (far longer than our oldest supported Flutter version). - Resolves a TODO about unregistering at engine shutdown, as it's been supported on `stable` for quite a while now. Fixes flutter/flutter#138427
1 parent 78f684c commit fc47eb7

File tree

15 files changed

+718
-1160
lines changed

15 files changed

+718
-1160
lines changed

packages/video_player/video_player_avfoundation/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
## NEXT
1+
## 2.5.7
22

3+
* Adds frame availability checks on iOS.
4+
* Simplifies internal platform channel interfaces.
35
* Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6.
46

57
## 2.5.6

packages/video_player/video_player_avfoundation/darwin/Classes/FVPVideoPlayerPlugin.m

Lines changed: 47 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,6 @@ @interface FVPFrameUpdater : NSObject
2323
@property(nonatomic, weak) AVPlayerItemVideoOutput *videoOutput;
2424
// The last time that has been validated as avaliable according to hasNewPixelBufferForItemTime:.
2525
@property(nonatomic, assign) CMTime lastKnownAvailableTime;
26-
// If YES, the engine is informed that a new texture is available any time the display link
27-
// callback is fired, regardless of the videoOutput state.
28-
//
29-
// TODO(stuartmorgan): Investigate removing this; it exists only to preserve existing iOS behavior
30-
// while implementing macOS, but iOS should very likely be doing the check as well. See
31-
// https://github.com/flutter/flutter/issues/138427.
32-
@property(nonatomic, assign) BOOL skipBufferAvailabilityCheck;
3326
@end
3427

3528
@implementation FVPFrameUpdater
@@ -42,18 +35,10 @@ - (FVPFrameUpdater *)initWithRegistry:(NSObject<FlutterTextureRegistry> *)regist
4235
}
4336

4437
- (void)displayLinkFired {
45-
// Only report a new frame if one is actually available, or the check is being skipped.
46-
BOOL reportFrame = NO;
47-
if (self.skipBufferAvailabilityCheck) {
48-
reportFrame = YES;
49-
} else {
50-
CMTime outputItemTime = [self.videoOutput itemTimeForHostTime:CACurrentMediaTime()];
51-
if ([self.videoOutput hasNewPixelBufferForItemTime:outputItemTime]) {
52-
_lastKnownAvailableTime = outputItemTime;
53-
reportFrame = YES;
54-
}
55-
}
56-
if (reportFrame) {
38+
// Only report a new frame if one is actually available.
39+
CMTime outputItemTime = [self.videoOutput itemTimeForHostTime:CACurrentMediaTime()];
40+
if ([self.videoOutput hasNewPixelBufferForItemTime:outputItemTime]) {
41+
_lastKnownAvailableTime = outputItemTime;
5742
[_registry textureFrameAvailable:_textureId];
5843
}
5944
}
@@ -334,10 +319,6 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item
334319
};
335320
_videoOutput = [avFactory videoOutputWithPixelBufferAttributes:pixBuffAttributes];
336321
frameUpdater.videoOutput = _videoOutput;
337-
#if TARGET_OS_IOS
338-
// See TODO on this property in FVPFrameUpdater.
339-
frameUpdater.skipBufferAvailabilityCheck = YES;
340-
#endif
341322

342323
[self addObserversForItem:item player:_player];
343324

@@ -703,14 +684,10 @@ - (instancetype)initWithAVFactory:(id<FVPAVFactory>)avFactory
703684
- (void)detachFromEngineForRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
704685
[self.playersByTextureId.allValues makeObjectsPerformSelector:@selector(disposeSansEventChannel)];
705686
[self.playersByTextureId removeAllObjects];
706-
// TODO(57151): This should be commented out when 57151's fix lands on stable.
707-
// This is the correct behavior we never did it in the past and the engine
708-
// doesn't currently support it.
709-
// FVPAVFoundationVideoPlayerApiSetup(registrar.messenger, nil);
687+
SetUpFVPAVFoundationVideoPlayerApi(registrar.messenger, nil);
710688
}
711689

712-
- (FVPTextureMessage *)onPlayerSetup:(FVPVideoPlayer *)player
713-
frameUpdater:(FVPFrameUpdater *)frameUpdater {
690+
- (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater *)frameUpdater {
714691
int64_t textureId = [self.registry registerTexture:player];
715692
frameUpdater.textureId = textureId;
716693
FlutterEventChannel *eventChannel = [FlutterEventChannel
@@ -725,8 +702,7 @@ - (FVPTextureMessage *)onPlayerSetup:(FVPVideoPlayer *)player
725702
// the engine is now expecting the texture to be populated.
726703
[player expectFrame];
727704

728-
FVPTextureMessage *result = [FVPTextureMessage makeWithTextureId:textureId];
729-
return result;
705+
return textureId;
730706
}
731707

732708
- (void)initialize:(FlutterError *__autoreleasing *)error {
@@ -743,7 +719,8 @@ - (void)initialize:(FlutterError *__autoreleasing *)error {
743719
[self.playersByTextureId removeAllObjects];
744720
}
745721

746-
- (FVPTextureMessage *)create:(FVPCreateMessage *)input error:(FlutterError **)error {
722+
- (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options
723+
error:(FlutterError **)error {
747724
FVPFrameUpdater *frameUpdater = [[FVPFrameUpdater alloc] initWithRegistry:_registry];
748725
FVPDisplayLink *displayLink =
749726
[self.displayLinkFactory displayLinkWithRegistrar:_registrar
@@ -752,110 +729,96 @@ - (FVPTextureMessage *)create:(FVPCreateMessage *)input error:(FlutterError **)e
752729
}];
753730

754731
FVPVideoPlayer *player;
755-
if (input.asset) {
732+
if (options.asset) {
756733
NSString *assetPath;
757-
if (input.packageName) {
758-
assetPath = [_registrar lookupKeyForAsset:input.asset fromPackage:input.packageName];
734+
if (options.packageName) {
735+
assetPath = [_registrar lookupKeyForAsset:options.asset fromPackage:options.packageName];
759736
} else {
760-
assetPath = [_registrar lookupKeyForAsset:input.asset];
737+
assetPath = [_registrar lookupKeyForAsset:options.asset];
761738
}
762739
@try {
763740
player = [[FVPVideoPlayer alloc] initWithAsset:assetPath
764741
frameUpdater:frameUpdater
765742
displayLink:displayLink
766743
avFactory:_avFactory
767744
registrar:self.registrar];
768-
return [self onPlayerSetup:player frameUpdater:frameUpdater];
745+
return @([self onPlayerSetup:player frameUpdater:frameUpdater]);
769746
} @catch (NSException *exception) {
770747
*error = [FlutterError errorWithCode:@"video_player" message:exception.reason details:nil];
771748
return nil;
772749
}
773-
} else if (input.uri) {
774-
player = [[FVPVideoPlayer alloc] initWithURL:[NSURL URLWithString:input.uri]
750+
} else if (options.uri) {
751+
player = [[FVPVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri]
775752
frameUpdater:frameUpdater
776753
displayLink:displayLink
777-
httpHeaders:input.httpHeaders
754+
httpHeaders:options.httpHeaders
778755
avFactory:_avFactory
779756
registrar:self.registrar];
780-
return [self onPlayerSetup:player frameUpdater:frameUpdater];
757+
return @([self onPlayerSetup:player frameUpdater:frameUpdater]);
781758
} else {
782759
*error = [FlutterError errorWithCode:@"video_player" message:@"not implemented" details:nil];
783760
return nil;
784761
}
785762
}
786763

787-
- (void)dispose:(FVPTextureMessage *)input error:(FlutterError **)error {
788-
NSNumber *playerKey = @(input.textureId);
764+
- (void)disposePlayer:(NSInteger)textureId error:(FlutterError **)error {
765+
NSNumber *playerKey = @(textureId);
789766
FVPVideoPlayer *player = self.playersByTextureId[playerKey];
790-
[self.registry unregisterTexture:input.textureId];
767+
[self.registry unregisterTexture:textureId];
791768
[self.playersByTextureId removeObjectForKey:playerKey];
792-
// If the Flutter contains https://github.com/flutter/engine/pull/12695,
793-
// the `player` is disposed via `onTextureUnregistered` at the right time.
794-
// Without https://github.com/flutter/engine/pull/12695, there is no guarantee that the
795-
// texture has completed the un-reregistration. It may leads a crash if we dispose the
796-
// `player` before the texture is unregistered. We add a dispatch_after hack to make sure the
797-
// texture is unregistered before we dispose the `player`.
798-
//
799-
// TODO(cyanglaz): Remove this dispatch block when
800-
// https://github.com/flutter/flutter/commit/8159a9906095efc9af8b223f5e232cb63542ad0b is in
801-
// stable And update the min flutter version of the plugin to the stable version.
802-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)),
803-
dispatch_get_main_queue(), ^{
804-
if (!player.disposed) {
805-
[player dispose];
806-
}
807-
});
769+
if (!player.disposed) {
770+
[player dispose];
771+
}
808772
}
809773

810-
- (void)setLooping:(FVPLoopingMessage *)input error:(FlutterError **)error {
811-
FVPVideoPlayer *player = self.playersByTextureId[@(input.textureId)];
812-
player.isLooping = input.isLooping;
774+
- (void)setLooping:(BOOL)isLooping forPlayer:(NSInteger)textureId error:(FlutterError **)error {
775+
FVPVideoPlayer *player = self.playersByTextureId[@(textureId)];
776+
player.isLooping = isLooping;
813777
}
814778

815-
- (void)setVolume:(FVPVolumeMessage *)input error:(FlutterError **)error {
816-
FVPVideoPlayer *player = self.playersByTextureId[@(input.textureId)];
817-
[player setVolume:input.volume];
779+
- (void)setVolume:(double)volume forPlayer:(NSInteger)textureId error:(FlutterError **)error {
780+
FVPVideoPlayer *player = self.playersByTextureId[@(textureId)];
781+
[player setVolume:volume];
818782
}
819783

820-
- (void)setPlaybackSpeed:(FVPPlaybackSpeedMessage *)input error:(FlutterError **)error {
821-
FVPVideoPlayer *player = self.playersByTextureId[@(input.textureId)];
822-
[player setPlaybackSpeed:input.speed];
784+
- (void)setPlaybackSpeed:(double)speed forPlayer:(NSInteger)textureId error:(FlutterError **)error {
785+
FVPVideoPlayer *player = self.playersByTextureId[@(textureId)];
786+
[player setPlaybackSpeed:speed];
823787
}
824788

825-
- (void)play:(FVPTextureMessage *)input error:(FlutterError **)error {
826-
FVPVideoPlayer *player = self.playersByTextureId[@(input.textureId)];
789+
- (void)playPlayer:(NSInteger)textureId error:(FlutterError **)error {
790+
FVPVideoPlayer *player = self.playersByTextureId[@(textureId)];
827791
[player play];
828792
}
829793

830-
- (FVPPositionMessage *)position:(FVPTextureMessage *)input error:(FlutterError **)error {
831-
FVPVideoPlayer *player = self.playersByTextureId[@(input.textureId)];
832-
FVPPositionMessage *result = [FVPPositionMessage makeWithTextureId:input.textureId
833-
position:[player position]];
834-
return result;
794+
- (nullable NSNumber *)positionForPlayer:(NSInteger)textureId error:(FlutterError **)error {
795+
FVPVideoPlayer *player = self.playersByTextureId[@(textureId)];
796+
return @([player position]);
835797
}
836798

837-
- (void)seekTo:(FVPPositionMessage *)input
838-
completion:(void (^)(FlutterError *_Nullable))completion {
839-
FVPVideoPlayer *player = self.playersByTextureId[@(input.textureId)];
840-
[player seekTo:input.position
799+
- (void)seekTo:(NSInteger)position
800+
forPlayer:(NSInteger)textureId
801+
completion:(nonnull void (^)(FlutterError *_Nullable))completion {
802+
FVPVideoPlayer *player = self.playersByTextureId[@(textureId)];
803+
[player seekTo:position
841804
completionHandler:^(BOOL finished) {
842805
dispatch_async(dispatch_get_main_queue(), ^{
843806
completion(nil);
844807
});
845808
}];
846809
}
847810

848-
- (void)pause:(FVPTextureMessage *)input error:(FlutterError **)error {
849-
FVPVideoPlayer *player = self.playersByTextureId[@(input.textureId)];
811+
- (void)pausePlayer:(NSInteger)textureId error:(FlutterError **)error {
812+
FVPVideoPlayer *player = self.playersByTextureId[@(textureId)];
850813
[player pause];
851814
}
852815

853-
- (void)setMixWithOthers:(FVPMixWithOthersMessage *)input
816+
- (void)setMixWithOthers:(BOOL)mixWithOthers
854817
error:(FlutterError *_Nullable __autoreleasing *)error {
855818
#if TARGET_OS_OSX
856819
// AVAudioSession doesn't exist on macOS, and audio always mixes, so just no-op.
857820
#else
858-
if (input.mixWithOthers) {
821+
if (mixWithOthers) {
859822
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
860823
withOptions:AVAudioSessionCategoryOptionMixWithOthers
861824
error:nil];
Lines changed: 26 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2013 The Flutter Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
4-
// Autogenerated from Pigeon (v13.0.0), do not edit directly.
4+
// Autogenerated from Pigeon (v18.0.0), do not edit directly.
55
// See also: https://pub.dev/packages/pigeon
66

77
#import <Foundation/Foundation.h>
@@ -13,54 +13,9 @@
1313

1414
NS_ASSUME_NONNULL_BEGIN
1515

16-
@class FVPTextureMessage;
17-
@class FVPLoopingMessage;
18-
@class FVPVolumeMessage;
19-
@class FVPPlaybackSpeedMessage;
20-
@class FVPPositionMessage;
21-
@class FVPCreateMessage;
22-
@class FVPMixWithOthersMessage;
16+
@class FVPCreationOptions;
2317

24-
@interface FVPTextureMessage : NSObject
25-
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
26-
- (instancetype)init NS_UNAVAILABLE;
27-
+ (instancetype)makeWithTextureId:(NSInteger)textureId;
28-
@property(nonatomic, assign) NSInteger textureId;
29-
@end
30-
31-
@interface FVPLoopingMessage : NSObject
32-
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
33-
- (instancetype)init NS_UNAVAILABLE;
34-
+ (instancetype)makeWithTextureId:(NSInteger)textureId isLooping:(BOOL)isLooping;
35-
@property(nonatomic, assign) NSInteger textureId;
36-
@property(nonatomic, assign) BOOL isLooping;
37-
@end
38-
39-
@interface FVPVolumeMessage : NSObject
40-
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
41-
- (instancetype)init NS_UNAVAILABLE;
42-
+ (instancetype)makeWithTextureId:(NSInteger)textureId volume:(double)volume;
43-
@property(nonatomic, assign) NSInteger textureId;
44-
@property(nonatomic, assign) double volume;
45-
@end
46-
47-
@interface FVPPlaybackSpeedMessage : NSObject
48-
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
49-
- (instancetype)init NS_UNAVAILABLE;
50-
+ (instancetype)makeWithTextureId:(NSInteger)textureId speed:(double)speed;
51-
@property(nonatomic, assign) NSInteger textureId;
52-
@property(nonatomic, assign) double speed;
53-
@end
54-
55-
@interface FVPPositionMessage : NSObject
56-
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
57-
- (instancetype)init NS_UNAVAILABLE;
58-
+ (instancetype)makeWithTextureId:(NSInteger)textureId position:(NSInteger)position;
59-
@property(nonatomic, assign) NSInteger textureId;
60-
@property(nonatomic, assign) NSInteger position;
61-
@end
62-
63-
@interface FVPCreateMessage : NSObject
18+
@interface FVPCreationOptions : NSObject
6419
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
6520
- (instancetype)init NS_UNAVAILABLE;
6621
+ (instancetype)makeWithAsset:(nullable NSString *)asset
@@ -75,38 +30,41 @@ NS_ASSUME_NONNULL_BEGIN
7530
@property(nonatomic, copy) NSDictionary<NSString *, NSString *> *httpHeaders;
7631
@end
7732

78-
@interface FVPMixWithOthersMessage : NSObject
79-
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
80-
- (instancetype)init NS_UNAVAILABLE;
81-
+ (instancetype)makeWithMixWithOthers:(BOOL)mixWithOthers;
82-
@property(nonatomic, assign) BOOL mixWithOthers;
83-
@end
84-
8533
/// The codec used by FVPAVFoundationVideoPlayerApi.
8634
NSObject<FlutterMessageCodec> *FVPAVFoundationVideoPlayerApiGetCodec(void);
8735

8836
@protocol FVPAVFoundationVideoPlayerApi
8937
- (void)initialize:(FlutterError *_Nullable *_Nonnull)error;
9038
/// @return `nil` only when `error != nil`.
91-
- (nullable FVPTextureMessage *)create:(FVPCreateMessage *)msg
92-
error:(FlutterError *_Nullable *_Nonnull)error;
93-
- (void)dispose:(FVPTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error;
94-
- (void)setLooping:(FVPLoopingMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error;
95-
- (void)setVolume:(FVPVolumeMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error;
96-
- (void)setPlaybackSpeed:(FVPPlaybackSpeedMessage *)msg
39+
- (nullable NSNumber *)createWithOptions:(FVPCreationOptions *)creationOptions
40+
error:(FlutterError *_Nullable *_Nonnull)error;
41+
- (void)disposePlayer:(NSInteger)textureId error:(FlutterError *_Nullable *_Nonnull)error;
42+
- (void)setLooping:(BOOL)isLooping
43+
forPlayer:(NSInteger)textureId
44+
error:(FlutterError *_Nullable *_Nonnull)error;
45+
- (void)setVolume:(double)volume
46+
forPlayer:(NSInteger)textureId
47+
error:(FlutterError *_Nullable *_Nonnull)error;
48+
- (void)setPlaybackSpeed:(double)speed
49+
forPlayer:(NSInteger)textureId
9750
error:(FlutterError *_Nullable *_Nonnull)error;
98-
- (void)play:(FVPTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error;
51+
- (void)playPlayer:(NSInteger)textureId error:(FlutterError *_Nullable *_Nonnull)error;
9952
/// @return `nil` only when `error != nil`.
100-
- (nullable FVPPositionMessage *)position:(FVPTextureMessage *)msg
101-
error:(FlutterError *_Nullable *_Nonnull)error;
102-
- (void)seekTo:(FVPPositionMessage *)msg completion:(void (^)(FlutterError *_Nullable))completion;
103-
- (void)pause:(FVPTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error;
104-
- (void)setMixWithOthers:(FVPMixWithOthersMessage *)msg
105-
error:(FlutterError *_Nullable *_Nonnull)error;
53+
- (nullable NSNumber *)positionForPlayer:(NSInteger)textureId
54+
error:(FlutterError *_Nullable *_Nonnull)error;
55+
- (void)seekTo:(NSInteger)position
56+
forPlayer:(NSInteger)textureId
57+
completion:(void (^)(FlutterError *_Nullable))completion;
58+
- (void)pausePlayer:(NSInteger)textureId error:(FlutterError *_Nullable *_Nonnull)error;
59+
- (void)setMixWithOthers:(BOOL)mixWithOthers error:(FlutterError *_Nullable *_Nonnull)error;
10660
@end
10761

10862
extern void SetUpFVPAVFoundationVideoPlayerApi(
10963
id<FlutterBinaryMessenger> binaryMessenger,
11064
NSObject<FVPAVFoundationVideoPlayerApi> *_Nullable api);
11165

66+
extern void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(
67+
id<FlutterBinaryMessenger> binaryMessenger,
68+
NSObject<FVPAVFoundationVideoPlayerApi> *_Nullable api, NSString *messageChannelSuffix);
69+
11270
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)