3
3
// found in the LICENSE file.
4
4
5
5
#import " FLTVideoPlayerPlugin.h"
6
+ #import " FLTVideoPlayerPlugin_Test.h"
6
7
7
8
#import < AVFoundation/AVFoundation.h>
8
9
#import < GLKit/GLKit.h>
@@ -33,6 +34,16 @@ - (void)onDisplayLink:(CADisplayLink *)link {
33
34
}
34
35
@end
35
36
37
+ @interface FVPDefaultPlayerFactory : NSObject <FVPPlayerFactory>
38
+ @end
39
+
40
+ @implementation FVPDefaultPlayerFactory
41
+ - (AVPlayer *)playerWithPlayerItem : (AVPlayerItem *)playerItem {
42
+ return [AVPlayer playerWithPlayerItem: playerItem];
43
+ }
44
+
45
+ @end
46
+
36
47
@interface FLTVideoPlayer : NSObject <FlutterTexture, FlutterStreamHandler>
37
48
@property (readonly , nonatomic ) AVPlayer *player;
38
49
@property (readonly , nonatomic ) AVPlayerItemVideoOutput *videoOutput;
@@ -52,7 +63,8 @@ @interface FLTVideoPlayer : NSObject <FlutterTexture, FlutterStreamHandler>
52
63
@property (nonatomic , readonly ) BOOL isInitialized;
53
64
- (instancetype )initWithURL : (NSURL *)url
54
65
frameUpdater : (FLTFrameUpdater *)frameUpdater
55
- httpHeaders : (nonnull NSDictionary <NSString *, NSString *> *)headers ;
66
+ httpHeaders : (nonnull NSDictionary <NSString *, NSString *> *)headers
67
+ playerFactory : (id <FVPPlayerFactory>)playerFactory ;
56
68
@end
57
69
58
70
static void *timeRangeContext = &timeRangeContext;
@@ -65,9 +77,14 @@ - (instancetype)initWithURL:(NSURL *)url
65
77
static void *rateContext = &rateContext;
66
78
67
79
@implementation FLTVideoPlayer
68
- - (instancetype )initWithAsset : (NSString *)asset frameUpdater : (FLTFrameUpdater *)frameUpdater {
80
+ - (instancetype )initWithAsset : (NSString *)asset
81
+ frameUpdater : (FLTFrameUpdater *)frameUpdater
82
+ playerFactory : (id <FVPPlayerFactory>)playerFactory {
69
83
NSString *path = [[NSBundle mainBundle ] pathForResource: asset ofType: nil ];
70
- return [self initWithURL: [NSURL fileURLWithPath: path] frameUpdater: frameUpdater httpHeaders: @{}];
84
+ return [self initWithURL: [NSURL fileURLWithPath: path]
85
+ frameUpdater: frameUpdater
86
+ httpHeaders: @{}
87
+ playerFactory: playerFactory];
71
88
}
72
89
73
90
- (void )addObserversForItem : (AVPlayerItem *)item player : (AVPlayer *)player {
@@ -203,18 +220,20 @@ - (void)createVideoOutputAndDisplayLink:(FLTFrameUpdater *)frameUpdater {
203
220
204
221
- (instancetype )initWithURL : (NSURL *)url
205
222
frameUpdater : (FLTFrameUpdater *)frameUpdater
206
- httpHeaders : (nonnull NSDictionary <NSString *, NSString *> *)headers {
223
+ httpHeaders : (nonnull NSDictionary <NSString *, NSString *> *)headers
224
+ playerFactory : (id <FVPPlayerFactory>)playerFactory {
207
225
NSDictionary <NSString *, id > *options = nil ;
208
226
if ([headers count ] != 0 ) {
209
227
options = @{@" AVURLAssetHTTPHeaderFieldsKey" : headers};
210
228
}
211
229
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL: url options: options];
212
230
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset: urlAsset];
213
- return [self initWithPlayerItem: item frameUpdater: frameUpdater];
231
+ return [self initWithPlayerItem: item frameUpdater: frameUpdater playerFactory: playerFactory ];
214
232
}
215
233
216
234
- (instancetype )initWithPlayerItem : (AVPlayerItem *)item
217
- frameUpdater : (FLTFrameUpdater *)frameUpdater {
235
+ frameUpdater : (FLTFrameUpdater *)frameUpdater
236
+ playerFactory : (id <FVPPlayerFactory>)playerFactory {
218
237
self = [super init ];
219
238
NSAssert (self, @" super init cannot be nil" );
220
239
@@ -247,7 +266,7 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item
247
266
}
248
267
};
249
268
250
- _player = [AVPlayer playerWithPlayerItem: item];
269
+ _player = [playerFactory playerWithPlayerItem: item];
251
270
_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
252
271
253
272
// This is to fix 2 bugs: 1. blank video for encrypted video streams on iOS 16
@@ -420,9 +439,15 @@ - (int64_t)duration {
420
439
}
421
440
422
441
- (void )seekTo : (int )location completionHandler : (void (^)(BOOL ))completionHandler {
423
- [_player seekToTime: CMTimeMake (location, 1000 )
424
- toleranceBefore: kCMTimeZero
425
- toleranceAfter: kCMTimeZero
442
+ CMTime locationCMT = CMTimeMake (location, 1000 );
443
+ CMTimeValue duration = _player.currentItem .asset .duration .value ;
444
+ // Without adding tolerance when seeking to duration,
445
+ // seekToTime will never complete, and this call will hang.
446
+ // see issue https://github.com/flutter/flutter/issues/124475.
447
+ CMTime tolerance = location == duration ? CMTimeMake (1 , 1000 ) : kCMTimeZero ;
448
+ [_player seekToTime: locationCMT
449
+ toleranceBefore: tolerance
450
+ toleranceAfter: tolerance
426
451
completionHandler: completionHandler];
427
452
}
428
453
@@ -523,6 +548,7 @@ @interface FLTVideoPlayerPlugin () <FLTAVFoundationVideoPlayerApi>
523
548
@property (readonly , strong , nonatomic )
524
549
NSMutableDictionary <NSNumber *, FLTVideoPlayer *> *playersByTextureId;
525
550
@property (readonly , strong , nonatomic ) NSObject <FlutterPluginRegistrar> *registrar;
551
+ @property (nonatomic , strong ) id <FVPPlayerFactory> playerFactory;
526
552
@end
527
553
528
554
@implementation FLTVideoPlayerPlugin
@@ -533,11 +559,17 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
533
559
}
534
560
535
561
- (instancetype )initWithRegistrar : (NSObject <FlutterPluginRegistrar> *)registrar {
562
+ return [self initWithPlayerFactory: [[FVPDefaultPlayerFactory alloc ] init ] registrar: registrar];
563
+ }
564
+
565
+ - (instancetype )initWithPlayerFactory : (id <FVPPlayerFactory>)playerFactory
566
+ registrar : (NSObject <FlutterPluginRegistrar> *)registrar {
536
567
self = [super init ];
537
568
NSAssert (self, @" super init cannot be nil" );
538
569
_registry = [registrar textures ];
539
570
_messenger = [registrar messenger ];
540
571
_registrar = registrar;
572
+ _playerFactory = playerFactory;
541
573
_playersByTextureId = [NSMutableDictionary dictionaryWithCapacity: 1 ];
542
574
return self;
543
575
}
@@ -588,12 +620,15 @@ - (FLTTextureMessage *)create:(FLTCreateMessage *)input error:(FlutterError **)e
588
620
} else {
589
621
assetPath = [_registrar lookupKeyForAsset: input.asset];
590
622
}
591
- player = [[FLTVideoPlayer alloc ] initWithAsset: assetPath frameUpdater: frameUpdater];
623
+ player = [[FLTVideoPlayer alloc ] initWithAsset: assetPath
624
+ frameUpdater: frameUpdater
625
+ playerFactory: _playerFactory];
592
626
return [self onPlayerSetup: player frameUpdater: frameUpdater];
593
627
} else if (input.uri ) {
594
628
player = [[FLTVideoPlayer alloc ] initWithURL: [NSURL URLWithString: input.uri]
595
629
frameUpdater: frameUpdater
596
- httpHeaders: input.httpHeaders];
630
+ httpHeaders: input.httpHeaders
631
+ playerFactory: _playerFactory];
597
632
return [self onPlayerSetup: player frameUpdater: frameUpdater];
598
633
} else {
599
634
*error = [FlutterError errorWithCode: @" video_player" message: @" not implemented" details: nil ];
0 commit comments