99#import < GLKit/GLKit.h>
1010
1111#import " AVAssetTrackUtils.h"
12+ #import " FVPDisplayLink.h"
1213#import " messages.g.h"
1314
1415#if !__has_feature(objc_arc)
@@ -22,9 +23,12 @@ @interface FVPFrameUpdater : NSObject
2223@property (nonatomic , weak ) AVPlayerItemVideoOutput *videoOutput;
2324// The last time that has been validated as avaliable according to hasNewPixelBufferForItemTime:.
2425@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;
2832@end
2933
3034@implementation FVPFrameUpdater
@@ -36,35 +40,24 @@ - (FVPFrameUpdater *)initWithRegistry:(NSObject<FlutterTextureRegistry> *)regist
3640 return self;
3741}
3842
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-
4743- (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) {
5256 [_registry textureFrameAvailable: _textureId];
5357 }
5458}
5559@end
5660
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-
6861@interface FVPDefaultPlayerFactory : NSObject <FVPPlayerFactory>
6962@end
7063
@@ -96,16 +89,7 @@ @interface FVPVideoPlayer : NSObject <FlutterTexture, FlutterStreamHandler>
9689@property (nonatomic ) BOOL isLooping;
9790@property (nonatomic , readonly ) BOOL isInitialized;
9891@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;
10993
11094- (instancetype )initWithURL : (NSURL *)url
11195 frameUpdater : (FVPFrameUpdater *)frameUpdater
@@ -255,27 +239,15 @@ - (void)createVideoOutputAndDisplayLink:(FVPFrameUpdater *)frameUpdater {
255239 };
256240 _videoOutput = [[AVPlayerItemVideoOutput alloc ] initWithPixelBufferAttributes: pixBuffAttributes];
257241
258- # if TARGET_OS_OSX
242+
259243 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 ;
278247#endif
248+ self.displayLink = [[FVPDisplayLink alloc ] initWithCallback: ^() {
249+ [frameUpdater displayLinkFired ];
250+ }];
279251}
280252
281253- (instancetype )initWithURL : (NSURL *)url
@@ -428,23 +400,7 @@ - (void)updatePlayingState {
428400 } else {
429401 [_player pause ];
430402 }
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;
448404}
449405
450406- (void )setupEventSinkIfReadyToPlay {
@@ -615,16 +571,7 @@ - (void)disposeSansEventChannel {
615571
616572 _disposed = YES ;
617573 [_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 ;
628575 [self removeKeyValueObservers ];
629576
630577 [self .player replaceCurrentItemWithPlayerItem: nil ];
0 commit comments