@@ -41,7 +41,7 @@ @interface FLTVideoPlayer : NSObject <FlutterTexture, FlutterStreamHandler>
4141@property (readonly , nonatomic ) AVPlayerItemVideoOutput* videoOutput;
4242@property (readonly , nonatomic ) CVDisplayLinkRef displayLink;
4343@property (readonly , nonatomic ) FLTFrameUpdater* frameUpdater;
44- @property (readonly , nonatomic ) CVPixelBufferRef _Nullable frame ;
44+ @property (readonly , nonatomic ) CVPixelBufferRef _Nullable lastValidFrame ;
4545@property (nonatomic ) FlutterEventChannel* eventChannel;
4646@property (nonatomic ) FlutterEventSink eventSink;
4747@property (nonatomic ) CGAffineTransform preferredTransform;
@@ -170,10 +170,10 @@ - (void)notifyIfFrameAvailable {
170170 if (!_playerItem || _playerItem.status != AVPlayerItemStatusReadyToPlay || ![_videoOutput hasNewPixelBufferForItemTime: outputItemTime]) {
171171 return ;
172172 } else {
173- CVBufferRelease (_frame) ;
174- _frame = [_videoOutput copyPixelBufferForItemTime: outputItemTime itemTimeForDisplay: NULL ];
175- if (_frame == NULL ) {
176- NSLog ( @" copyPixelBufferForItemTime returned NULL " ) ;
173+ CVPixelBufferRef pixelBuffer = [_videoOutput copyPixelBufferForItemTime: outputItemTime itemTimeForDisplay: NULL ] ;
174+ if (pixelBuffer != NULL ) {
175+ CVBufferRelease (_lastValidFrame);
176+ _lastValidFrame = pixelBuffer ;
177177 }
178178 [_frameUpdater notifyFrameAvailable ];
179179 }
@@ -441,11 +441,32 @@ - (void)setPlaybackSpeed:(double)speed {
441441}
442442
443443- (CVPixelBufferRef)copyPixelBuffer {
444- if (_frame == NULL ) {
445- NSLog (@" Returning NULL from copyPixelBuffer" );
446- }
447- CVBufferRetain (_frame);
448- return _frame;
444+ // Creates a memcpy of the last valid frame.
445+ //
446+ // Unlike on iOS, the macOS embedder does show the last frame when
447+ // we return NULL from `copyPixelBuffer`.
448+ CVPixelBufferLockBaseAddress (_lastValidFrame, kCVPixelBufferLock_ReadOnly );
449+ int bufferWidth = (int )CVPixelBufferGetWidth (_lastValidFrame);
450+ int bufferHeight = (int )CVPixelBufferGetHeight (_lastValidFrame);
451+ size_t bytesPerRow = CVPixelBufferGetBytesPerRow (_lastValidFrame);
452+ uint8_t *baseAddress = CVPixelBufferGetBaseAddress (_lastValidFrame);
453+ NSDictionary * pixBuffAttributes = @{
454+ (id )kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA ),
455+ (id )kCVPixelBufferIOSurfacePropertiesKey : @{},
456+ (id )kCVPixelBufferOpenGLCompatibilityKey : @YES ,
457+ (id )kCVPixelBufferMetalCompatibilityKey : @YES ,
458+ };
459+
460+ CVPixelBufferRef pixelBufferCopy = NULL ;
461+ CVPixelBufferCreate (kCFAllocatorDefault , bufferWidth, bufferHeight,kCVPixelFormatType_32BGRA ,
462+ CFBridgingRetain (pixBuffAttributes), &pixelBufferCopy);
463+ CVPixelBufferLockBaseAddress (pixelBufferCopy, 0 );
464+ uint8_t *copyBaseAddress = CVPixelBufferGetBaseAddress (pixelBufferCopy);
465+ memcpy (copyBaseAddress, baseAddress, bufferHeight * bytesPerRow);
466+
467+ CVPixelBufferUnlockBaseAddress (_lastValidFrame, kCVPixelBufferLock_ReadOnly );
468+ CVPixelBufferUnlockBaseAddress (pixelBufferCopy, 0 );
469+ return pixelBufferCopy;
449470}
450471
451472- (void )onTextureUnregistered : (NSObject <FlutterTexture>*)texture {
@@ -476,7 +497,7 @@ - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
476497// / so the channel is going to die or is already dead.
477498- (void )disposeSansEventChannel {
478499 _disposed = true ;
479- CVBufferRelease (_frame );
500+ CVBufferRelease (_lastValidFrame );
480501 [self stopDisplayLink ];
481502 [[_player currentItem ] removeObserver: self forKeyPath: @" status" context: statusContext];
482503 [[_player currentItem ] removeObserver: self
0 commit comments