Skip to content

Commit 994df11

Browse files
Inject view provider for video_player_avfoundation
1 parent 60ce03a commit 994df11

File tree

11 files changed

+160
-52
lines changed

11 files changed

+160
-52
lines changed

packages/video_player/video_player_avfoundation/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## NEXT
22

33
* Updates minimum supported SDK version to Flutter 3.27/Dart 3.6.
4+
* Refactors native code for improved testing.
45

56
## 2.7.1
67

packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,26 @@ - (void)seekToTime:(CMTime)time
8282

8383
@end
8484

85+
// Convience to avoid having two copies of the StubViewProvider code.
86+
#if TARGET_OS_OSX
87+
#define PROVIDED_VIEW_TYPE NSView
88+
#else
89+
#define PROVIDED_VIEW_TYPE UIView
90+
#endif
91+
92+
@interface StubViewProvider : NSObject <FVPViewProvider>
93+
- (instancetype)initWithView:(PROVIDED_VIEW_TYPE *)view;
94+
@property(nonatomic, nullable) PROVIDED_VIEW_TYPE *view;
95+
@end
96+
97+
@implementation StubViewProvider
98+
- (instancetype)initWithView:(PROVIDED_VIEW_TYPE *)view {
99+
self = [super init];
100+
_view = view;
101+
return self;
102+
}
103+
@end
104+
85105
@interface StubFVPAVFactory : NSObject <FVPAVFactory>
86106

87107
@property(nonatomic, strong) StubAVPlayer *stubAVPlayer;
@@ -190,9 +210,18 @@ - (void)testBlankVideoBugWithEncryptedVideoStreamAndInvertedAspectRatioBugForSom
190210
// video streams (not just iOS 16). (https://github.com/flutter/flutter/issues/109116). An
191211
// invisible AVPlayerLayer is used to overwrite the protection of pixel buffers in those streams
192212
// for issue #1, and restore the correct width and height for issue #2.
213+
#if TARGET_OS_OSX
214+
NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 10, 10)];
215+
view.wantsLayer = true;
216+
#else
217+
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
218+
#endif
193219
NSObject<FlutterPluginRegistrar> *registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar));
194-
FVPVideoPlayerPlugin *videoPlayerPlugin =
195-
[[FVPVideoPlayerPlugin alloc] initWithRegistrar:registrar];
220+
FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc]
221+
initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:nil]
222+
displayLinkFactory:nil
223+
viewProvider:[[StubViewProvider alloc] initWithView:view]
224+
registrar:registrar];
196225

197226
FlutterError *error;
198227
[videoPlayerPlugin initialize:&error];
@@ -213,7 +242,8 @@ - (void)testBlankVideoBugWithEncryptedVideoStreamAndInvertedAspectRatioBugForSom
213242
XCTAssertNotNil(player);
214243

215244
XCTAssertNotNil(player.playerLayer, @"AVPlayerLayer should be present.");
216-
XCTAssertNotNil(player.playerLayer.superlayer, @"AVPlayerLayer should be added on screen.");
245+
XCTAssertEqual(player.playerLayer.superlayer, view.layer,
246+
@"AVPlayerLayer should be added on screen.");
217247
}
218248

219249
- (void)testPlayerForPlatformViewDoesNotRegisterTexture {
@@ -231,6 +261,7 @@ - (void)testPlayerForPlatformViewDoesNotRegisterTexture {
231261
FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc]
232262
initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:mockVideoOutput]
233263
displayLinkFactory:stubDisplayLinkFactory
264+
viewProvider:[[StubViewProvider alloc] initWithView:nil]
234265
registrar:registrar];
235266

236267
FlutterError *initalizationError;
@@ -264,6 +295,7 @@ - (void)testSeekToWhilePausedStartsDisplayLinkTemporarily {
264295
FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc]
265296
initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:mockVideoOutput]
266297
displayLinkFactory:stubDisplayLinkFactory
298+
viewProvider:[[StubViewProvider alloc] initWithView:nil]
267299
registrar:registrar];
268300

269301
FlutterError *initalizationError;
@@ -335,6 +367,7 @@ - (void)testInitStartsDisplayLinkTemporarily {
335367
initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:stubAVPlayer
336368
output:mockVideoOutput]
337369
displayLinkFactory:stubDisplayLinkFactory
370+
viewProvider:[[StubViewProvider alloc] initWithView:nil]
338371
registrar:registrar];
339372

340373
FlutterError *initalizationError;
@@ -386,6 +419,7 @@ - (void)testSeekToWhilePlayingDoesNotStopDisplayLink {
386419
FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc]
387420
initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:mockVideoOutput]
388421
displayLinkFactory:stubDisplayLinkFactory
422+
viewProvider:[[StubViewProvider alloc] initWithView:nil]
389423
registrar:registrar];
390424

391425
FlutterError *initalizationError;
@@ -454,6 +488,7 @@ - (void)testPauseWhileWaitingForFrameDoesNotStopDisplayLink {
454488
FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc]
455489
initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:mockVideoOutput]
456490
displayLinkFactory:stubDisplayLinkFactory
491+
viewProvider:[[StubViewProvider alloc] initWithView:nil]
457492
registrar:registrar];
458493

459494
FlutterError *initalizationError;
@@ -635,6 +670,7 @@ - (void)testSeekToleranceWhenNotSeekingToEnd {
635670
FVPVideoPlayerPlugin *pluginWithMockAVPlayer =
636671
[[FVPVideoPlayerPlugin alloc] initWithAVFactory:stubAVFactory
637672
displayLinkFactory:nil
673+
viewProvider:[[StubViewProvider alloc] initWithView:nil]
638674
registrar:registrar];
639675

640676
FlutterError *initializationError;
@@ -673,6 +709,7 @@ - (void)testSeekToleranceWhenSeekingToEnd {
673709
FVPVideoPlayerPlugin *pluginWithMockAVPlayer =
674710
[[FVPVideoPlayerPlugin alloc] initWithAVFactory:stubAVFactory
675711
displayLinkFactory:nil
712+
viewProvider:[[StubViewProvider alloc] initWithView:nil]
676713
registrar:registrar];
677714

678715
FlutterError *initializationError;
@@ -931,6 +968,7 @@ - (void)testUpdatePlayingStateShouldNotResetRate {
931968
FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc]
932969
initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:nil]
933970
displayLinkFactory:nil
971+
viewProvider:[[StubViewProvider alloc] initWithView:nil]
934972
registrar:registrar];
935973

936974
FlutterError *error;
@@ -975,6 +1013,7 @@ - (void)testPlayerShouldNotDropEverySecondFrame {
9751013
FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc]
9761014
initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:mockVideoOutput]
9771015
displayLinkFactory:stubDisplayLinkFactory
1016+
viewProvider:[[StubViewProvider alloc] initWithView:nil]
9781017
registrar:registrar];
9791018

9801019
FlutterError *error;

packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPTextureBasedVideoPlayer.m

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
#import "./include/video_player_avfoundation/FVPTextureBasedVideoPlayer_Test.h"
77

88
@interface FVPTextureBasedVideoPlayer ()
9-
// The CALayer associated with the Flutter view this plugin is associated with, if any.
10-
@property(nonatomic, readonly) CALayer *flutterViewLayer;
119
// The updater that drives callbacks to the engine to indicate that a new frame is ready.
1210
@property(nonatomic) FVPFrameUpdater *frameUpdater;
1311
// The display link that drives frameUpdater.
@@ -38,14 +36,14 @@ - (instancetype)initWithAsset:(NSString *)asset
3836
frameUpdater:(FVPFrameUpdater *)frameUpdater
3937
displayLink:(FVPDisplayLink *)displayLink
4038
avFactory:(id<FVPAVFactory>)avFactory
41-
registrar:(NSObject<FlutterPluginRegistrar> *)registrar
39+
viewProvider:(NSObject<FVPViewProvider> *)viewProvider
4240
onDisposed:(void (^)(int64_t))onDisposed {
4341
return [self initWithURL:[NSURL fileURLWithPath:[FVPVideoPlayer absolutePathForAssetName:asset]]
4442
frameUpdater:frameUpdater
4543
displayLink:displayLink
4644
httpHeaders:@{}
4745
avFactory:avFactory
48-
registrar:registrar
46+
viewProvider:viewProvider
4947
onDisposed:onDisposed];
5048
}
5149

@@ -54,7 +52,7 @@ - (instancetype)initWithURL:(NSURL *)url
5452
displayLink:(FVPDisplayLink *)displayLink
5553
httpHeaders:(nonnull NSDictionary<NSString *, NSString *> *)headers
5654
avFactory:(id<FVPAVFactory>)avFactory
57-
registrar:(NSObject<FlutterPluginRegistrar> *)registrar
55+
viewProvider:(NSObject<FVPViewProvider> *)viewProvider
5856
onDisposed:(void (^)(int64_t))onDisposed {
5957
NSDictionary<NSString *, id> *options = nil;
6058
if ([headers count] != 0) {
@@ -66,17 +64,17 @@ - (instancetype)initWithURL:(NSURL *)url
6664
frameUpdater:frameUpdater
6765
displayLink:displayLink
6866
avFactory:avFactory
69-
registrar:registrar
67+
viewProvider:viewProvider
7068
onDisposed:onDisposed];
7169
}
7270

7371
- (instancetype)initWithPlayerItem:(AVPlayerItem *)item
7472
frameUpdater:(FVPFrameUpdater *)frameUpdater
7573
displayLink:(FVPDisplayLink *)displayLink
7674
avFactory:(id<FVPAVFactory>)avFactory
77-
registrar:(NSObject<FlutterPluginRegistrar> *)registrar
75+
viewProvider:(NSObject<FVPViewProvider> *)viewProvider
7876
onDisposed:(void (^)(int64_t))onDisposed {
79-
self = [super initWithPlayerItem:item avFactory:avFactory registrar:registrar];
77+
self = [super initWithPlayerItem:item avFactory:avFactory viewProvider:viewProvider];
8078

8179
if (self) {
8280
_frameUpdater = frameUpdater;
@@ -91,7 +89,7 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item
9189
// invisible AVPlayerLayer is used to overwrite the protection of pixel buffers in those streams
9290
// for issue #1, and restore the correct width and height for issue #2.
9391
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
94-
[self.flutterViewLayer addSublayer:self.playerLayer];
92+
[viewProvider.view.layer addSublayer:self.playerLayer];
9593
}
9694
return self;
9795
}
@@ -110,22 +108,6 @@ - (void)expectFrame {
110108
_displayLink.running = YES;
111109
}
112110

113-
#pragma mark - Private methods
114-
115-
- (CALayer *)flutterViewLayer {
116-
#if TARGET_OS_OSX
117-
return self.registrar.view.layer;
118-
#else
119-
#pragma clang diagnostic push
120-
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
121-
// TODO(hellohuanlin): Provide a non-deprecated codepath. See
122-
// https://github.com/flutter/flutter/issues/104117
123-
UIViewController *root = UIApplication.sharedApplication.keyWindow.rootViewController;
124-
#pragma clang diagnostic pop
125-
return root.view.layer;
126-
#endif
127-
}
128-
129111
#pragma mark - Overrides
130112

131113
- (void)updatePlayingState {

packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,33 +20,33 @@
2020
@implementation FVPVideoPlayer
2121
- (instancetype)initWithAsset:(NSString *)asset
2222
avFactory:(id<FVPAVFactory>)avFactory
23-
registrar:(NSObject<FlutterPluginRegistrar> *)registrar {
23+
viewProvider:(NSObject<FVPViewProvider> *)viewProvider {
2424
return [self initWithURL:[NSURL fileURLWithPath:[FVPVideoPlayer absolutePathForAssetName:asset]]
2525
httpHeaders:@{}
2626
avFactory:avFactory
27-
registrar:registrar];
27+
viewProvider:viewProvider];
2828
}
2929

3030
- (instancetype)initWithURL:(NSURL *)url
3131
httpHeaders:(nonnull NSDictionary<NSString *, NSString *> *)headers
3232
avFactory:(id<FVPAVFactory>)avFactory
33-
registrar:(NSObject<FlutterPluginRegistrar> *)registrar {
33+
viewProvider:(NSObject<FVPViewProvider> *)viewProvider {
3434
NSDictionary<NSString *, id> *options = nil;
3535
if ([headers count] != 0) {
3636
options = @{@"AVURLAssetHTTPHeaderFieldsKey" : headers};
3737
}
3838
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:options];
3939
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:urlAsset];
40-
return [self initWithPlayerItem:item avFactory:avFactory registrar:registrar];
40+
return [self initWithPlayerItem:item avFactory:avFactory viewProvider:viewProvider];
4141
}
4242

4343
- (instancetype)initWithPlayerItem:(AVPlayerItem *)item
4444
avFactory:(id<FVPAVFactory>)avFactory
45-
registrar:(NSObject<FlutterPluginRegistrar> *)registrar {
45+
viewProvider:(NSObject<FVPViewProvider> *)viewProvider {
4646
self = [super init];
4747
NSAssert(self, @"super init cannot be nil");
4848

49-
_registrar = registrar;
49+
_viewProvider = viewProvider;
5050

5151
AVAsset *asset = [item asset];
5252
void (^assetCompletionHandler)(void) = ^{

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ @interface FVPVideoPlayerPlugin ()
4545
@property(readonly, strong, nonatomic) NSObject<FlutterPluginRegistrar> *registrar;
4646
@property(nonatomic, strong) id<FVPDisplayLinkFactory> displayLinkFactory;
4747
@property(nonatomic, strong) id<FVPAVFactory> avFactory;
48+
@property(nonatomic, strong) NSObject<FVPViewProvider> *viewProvider;
4849
// TODO(stuartmorgan): Decouple identifiers for platform views and texture views.
4950
@property(nonatomic, assign) int64_t nextNonTexturePlayerIdentifier;
5051
@end
@@ -68,19 +69,23 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
6869
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
6970
return [self initWithAVFactory:[[FVPDefaultAVFactory alloc] init]
7071
displayLinkFactory:[[FVPDefaultDisplayLinkFactory alloc] init]
72+
viewProvider:[[FVPDefaultViewProvider alloc] initWithRegistrar:registrar]
7173
registrar:registrar];
7274
}
7375

7476
- (instancetype)initWithAVFactory:(id<FVPAVFactory>)avFactory
7577
displayLinkFactory:(id<FVPDisplayLinkFactory>)displayLinkFactory
78+
viewProvider:(NSObject<FVPViewProvider> *)viewProvider
7679
registrar:(NSObject<FlutterPluginRegistrar> *)registrar {
7780
self = [super init];
7881
NSAssert(self, @"super init cannot be nil");
7982
_registry = [registrar textures];
8083
_messenger = [registrar messenger];
8184
_registrar = registrar;
85+
_viewProvider = viewProvider;
8286
_displayLinkFactory = displayLinkFactory ?: [[FVPDefaultDisplayLinkFactory alloc] init];
8387
_avFactory = avFactory ?: [[FVPDefaultAVFactory alloc] init];
88+
_viewProvider = viewProvider ?: [[FVPDefaultViewProvider alloc] initWithRegistrar:registrar];
8489
_playersByIdentifier = [NSMutableDictionary dictionaryWithCapacity:1];
8590
// Initialized to a high number to avoid collisions with texture identifiers (which are generated
8691
// separately).
@@ -209,16 +214,16 @@ - (nullable FVPTextureBasedVideoPlayer *)texturePlayerWithOptions:
209214
return [[FVPTextureBasedVideoPlayer alloc] initWithAsset:assetPath
210215
frameUpdater:frameUpdater
211216
displayLink:displayLink
212-
avFactory:_avFactory
213-
registrar:self.registrar
217+
avFactory:self.avFactory
218+
viewProvider:self.viewProvider
214219
onDisposed:onDisposed];
215220
} else if (options.uri) {
216221
return [[FVPTextureBasedVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri]
217222
frameUpdater:frameUpdater
218223
displayLink:displayLink
219224
httpHeaders:options.httpHeaders
220-
avFactory:_avFactory
221-
registrar:self.registrar
225+
avFactory:self.avFactory
226+
viewProvider:self.viewProvider
222227
onDisposed:onDisposed];
223228
}
224229

@@ -230,13 +235,13 @@ - (nullable FVPVideoPlayer *)platformViewPlayerWithOptions:(nonnull FVPCreationO
230235
if (options.asset) {
231236
NSString *assetPath = [self assetPathFromCreationOptions:options];
232237
return [[FVPVideoPlayer alloc] initWithAsset:assetPath
233-
avFactory:_avFactory
234-
registrar:self.registrar];
238+
avFactory:self.avFactory
239+
viewProvider:self.viewProvider];
235240
} else if (options.uri) {
236241
return [[FVPVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri]
237242
httpHeaders:options.httpHeaders
238-
avFactory:_avFactory
239-
registrar:self.registrar];
243+
avFactory:self.avFactory
244+
viewProvider:self.viewProvider];
240245
}
241246

242247
return nil;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import "./include/video_player_avfoundation/FVPViewProvider.h"
6+
7+
#if TARGET_OS_OSX
8+
@import Cocoa;
9+
#else
10+
@import UIKit;
11+
#endif
12+
13+
@interface FVPDefaultViewProvider ()
14+
/// The backing registrar.
15+
@property(nonatomic) NSObject<FlutterPluginRegistrar> *registrar;
16+
@end
17+
18+
@implementation FVPDefaultViewProvider
19+
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
20+
self = [super init];
21+
if (self) {
22+
_registrar = registrar;
23+
}
24+
return self;
25+
}
26+
27+
#if TARGET_OS_OSX
28+
- (NSView *)view {
29+
return self.registrar.view;
30+
}
31+
#else
32+
- (UIView *)view {
33+
#pragma clang diagnostic push
34+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
35+
// TODO(hellohuanlin): Provide a non-deprecated codepath. See
36+
// https://github.com/flutter/flutter/issues/104117
37+
UIViewController *root = UIApplication.sharedApplication.keyWindow.rootViewController;
38+
#pragma clang diagnostic pop
39+
return root.view;
40+
}
41+
#endif
42+
@end

packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPTextureBasedVideoPlayer.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN
2121
displayLink:(FVPDisplayLink *)displayLink
2222
httpHeaders:(nonnull NSDictionary<NSString *, NSString *> *)headers
2323
avFactory:(id<FVPAVFactory>)avFactory
24-
registrar:(NSObject<FlutterPluginRegistrar> *)registrar
24+
viewProvider:(NSObject<FVPViewProvider> *)viewProvider
2525
onDisposed:(void (^)(int64_t))onDisposed;
2626

2727
/// Initializes a new instance of FVPTextureBasedVideoPlayer with the given asset, frame updater,
@@ -30,7 +30,7 @@ NS_ASSUME_NONNULL_BEGIN
3030
frameUpdater:(FVPFrameUpdater *)frameUpdater
3131
displayLink:(FVPDisplayLink *)displayLink
3232
avFactory:(id<FVPAVFactory>)avFactory
33-
registrar:(NSObject<FlutterPluginRegistrar> *)registrar
33+
viewProvider:(NSObject<FVPViewProvider> *)viewProvider
3434
onDisposed:(void (^)(int64_t))onDisposed;
3535

3636
/// Sets the texture Identifier for the frame updater. This method should be called once the texture

0 commit comments

Comments
 (0)