9
9
#import < GLKit/GLKit.h>
10
10
11
11
#import " AVAssetTrackUtils.h"
12
+ #import " FVPDisplayLink.h"
12
13
#import " messages.g.h"
13
14
14
15
#if !__has_feature(objc_arc)
@@ -22,9 +23,12 @@ @interface FVPFrameUpdater : NSObject
22
23
@property (nonatomic , weak ) AVPlayerItemVideoOutput *videoOutput;
23
24
// The last time that has been validated as avaliable according to hasNewPixelBufferForItemTime:.
24
25
@property (nonatomic , assign ) CMTime lastKnownAvailableTime;
25
- #if TARGET_OS_IOS
26
- - (void )onDisplayLink : (CADisplayLink *)link ;
27
- #endif
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.
31
+ @property (nonatomic , assign ) BOOL skipBufferAvailabilityCheck;
28
32
@end
29
33
30
34
@implementation FVPFrameUpdater
@@ -36,35 +40,24 @@ - (FVPFrameUpdater *)initWithRegistry:(NSObject<FlutterTextureRegistry> *)regist
36
40
return self;
37
41
}
38
42
39
- #if TARGET_OS_IOS
40
- - (void )onDisplayLink : (CADisplayLink *)link {
41
- // TODO(stuartmorgan): Investigate switching this to displayLinkFired; iOS may also benefit from
42
- // the availability check there.
43
- [_registry textureFrameAvailable: _textureId];
44
- }
45
- #endif
46
-
47
43
- (void )displayLinkFired {
48
- // Only report a new frame if one is actually available.
49
- CMTime outputItemTime = [self .videoOutput itemTimeForHostTime: CACurrentMediaTime ()];
50
- if ([self .videoOutput hasNewPixelBufferForItemTime: outputItemTime]) {
51
- _lastKnownAvailableTime = outputItemTime;
44
+ // Only report a new frame if one is actually available, or the check is being skipped.
45
+ BOOL reportFrame = NO ;
46
+ if (self.skipBufferAvailabilityCheck ) {
47
+ reportFrame = YES ;
48
+ } else {
49
+ CMTime outputItemTime = [self .videoOutput itemTimeForHostTime: CACurrentMediaTime ()];
50
+ if ([self .videoOutput hasNewPixelBufferForItemTime: outputItemTime]) {
51
+ _lastKnownAvailableTime = outputItemTime;
52
+ reportFrame = YES ;
53
+ }
54
+ }
55
+ if (reportFrame) {
52
56
[_registry textureFrameAvailable: _textureId];
53
57
}
54
58
}
55
59
@end
56
60
57
- #if TARGET_OS_OSX
58
- static CVReturn DisplayLinkCallback (CVDisplayLinkRef displayLink, const CVTimeStamp *now,
59
- const CVTimeStamp *outputTime, CVOptionFlags flagsIn,
60
- CVOptionFlags *flagsOut, void *displayLinkSource) {
61
- // Trigger the main-thread dispatch queue, to drive a frame update check.
62
- __weak dispatch_source_t source = (__bridge dispatch_source_t )displayLinkSource;
63
- dispatch_source_merge_data (source, 1 );
64
- return kCVReturnSuccess ;
65
- }
66
- #endif
67
-
68
61
@interface FVPDefaultPlayerFactory : NSObject <FVPPlayerFactory>
69
62
@end
70
63
@@ -96,16 +89,7 @@ @interface FVPVideoPlayer : NSObject <FlutterTexture, FlutterStreamHandler>
96
89
@property (nonatomic ) BOOL isLooping;
97
90
@property (nonatomic , readonly ) BOOL isInitialized;
98
91
@property (nonatomic ) FVPFrameUpdater *frameUpdater;
99
- // TODO(stuartmorgan): Extract and abstract the display link to remove all the display-link-related
100
- // ifdefs from this file.
101
- #if TARGET_OS_OSX
102
- // The display link to trigger frame reads from the video player.
103
- @property (nonatomic , assign ) CVDisplayLinkRef displayLink;
104
- // A dispatch source to move display link callbacks to the main thread.
105
- @property (nonatomic , strong ) dispatch_source_t displayLinkSource;
106
- #else
107
- @property (nonatomic ) CADisplayLink *displayLink;
108
- #endif
92
+ @property (nonatomic ) FVPDisplayLink *displayLink;
109
93
110
94
- (instancetype )initWithURL : (NSURL *)url
111
95
frameUpdater : (FVPFrameUpdater *)frameUpdater
@@ -255,27 +239,15 @@ - (void)createVideoOutputAndDisplayLink:(FVPFrameUpdater *)frameUpdater {
255
239
};
256
240
_videoOutput = [[AVPlayerItemVideoOutput alloc ] initWithPixelBufferAttributes: pixBuffAttributes];
257
241
258
- # if TARGET_OS_OSX
242
+
259
243
frameUpdater.videoOutput = _videoOutput;
260
- // Create and start the main-thread dispatch queue to drive frameUpdater.
261
- self.displayLinkSource =
262
- dispatch_source_create (DISPATCH_SOURCE_TYPE_DATA_ADD, 0 , 0 , dispatch_get_main_queue ());
263
- dispatch_source_set_event_handler (self.displayLinkSource , ^() {
264
- @autoreleasepool {
265
- [frameUpdater displayLinkFired ];
266
- }
267
- });
268
- dispatch_resume (self.displayLinkSource );
269
- if (CVDisplayLinkCreateWithActiveCGDisplays (&_displayLink) == kCVReturnSuccess ) {
270
- CVDisplayLinkSetOutputCallback (_displayLink, &DisplayLinkCallback,
271
- (__bridge void *)(self.displayLinkSource ));
272
- }
273
- #else
274
- _displayLink = [CADisplayLink displayLinkWithTarget: frameUpdater
275
- selector: @selector (onDisplayLink: )];
276
- [_displayLink addToRunLoop: [NSRunLoop currentRunLoop ] forMode: NSRunLoopCommonModes ];
277
- _displayLink.paused = YES ;
244
+ #if TARGET_OS_IOS
245
+ // See TODO on this property in FVPFrameUpdater.
246
+ frameUpdater.skipBufferAvailabilityCheck = YES ;
278
247
#endif
248
+ self.displayLink = [[FVPDisplayLink alloc ] initWithCallback: ^() {
249
+ [frameUpdater displayLinkFired ];
250
+ }];
279
251
}
280
252
281
253
- (instancetype )initWithURL : (NSURL *)url
@@ -428,23 +400,7 @@ - (void)updatePlayingState {
428
400
} else {
429
401
[_player pause ];
430
402
}
431
- #if TARGET_OS_OSX
432
- if (_displayLink) {
433
- if (_isPlaying) {
434
- NSScreen *screen = self.registrar .view .window .screen ;
435
- if (screen) {
436
- CGDirectDisplayID viewDisplayID =
437
- (CGDirectDisplayID )[screen.deviceDescription[@" NSScreenNumber" ] unsignedIntegerValue ];
438
- CVDisplayLinkSetCurrentCGDisplay (_displayLink, viewDisplayID);
439
- }
440
- CVDisplayLinkStart (_displayLink);
441
- } else {
442
- CVDisplayLinkStop (_displayLink);
443
- }
444
- }
445
- #else
446
- _displayLink.paused = !_isPlaying;
447
- #endif
403
+ _displayLink.running = _isPlaying;
448
404
}
449
405
450
406
- (void )setupEventSinkIfReadyToPlay {
@@ -615,16 +571,7 @@ - (void)disposeSansEventChannel {
615
571
616
572
_disposed = YES ;
617
573
[_playerLayer removeFromSuperlayer ];
618
- #if TARGET_OS_OSX
619
- if (_displayLink) {
620
- CVDisplayLinkStop (_displayLink);
621
- CVDisplayLinkRelease (_displayLink);
622
- _displayLink = NULL ;
623
- }
624
- dispatch_source_cancel (_displayLinkSource);
625
- #else
626
- [_displayLink invalidate ];
627
- #endif
574
+ _displayLink = nil ;
628
575
[self removeKeyValueObservers ];
629
576
630
577
[self .player replaceCurrentItemWithPlayerItem: nil ];
0 commit comments