Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 35d8ec2

Browse files
committed
Return a copy of the last valid frame
1 parent 9e9aec7 commit 35d8ec2

File tree

1 file changed

+48
-16
lines changed

1 file changed

+48
-16
lines changed

packages/video_player/video_player_macos/macos/Classes/FLTVideoPlayerPlugin.m

+48-16
Original file line numberDiff line numberDiff line change
@@ -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,12 @@ - (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+
@synchronized (self) {
176+
CVBufferRelease(_lastValidFrame);
177+
_lastValidFrame = pixelBuffer;
178+
}
177179
}
178180
[_frameUpdater notifyFrameAvailable];
179181
}
@@ -185,9 +187,7 @@ static CVReturn OnDisplayLink(CVDisplayLinkRef CV_NONNULL displayLink,
185187
CVOptionFlags* CV_NONNULL flagsOut,
186188
void* CV_NULLABLE displayLinkContext) {
187189
__weak FLTVideoPlayer* video_player = (__bridge FLTVideoPlayer*)(displayLinkContext);
188-
dispatch_async(dispatch_get_main_queue(), ^{
189-
[video_player notifyIfFrameAvailable];
190-
});
190+
[video_player notifyIfFrameAvailable];
191191
return kCVReturnSuccess;
192192
}
193193

@@ -403,9 +403,7 @@ - (void)seekTo:(int)location {
403403
[_player seekToTime:CMTimeMake(location, 1000)
404404
toleranceBefore:kCMTimeZero
405405
toleranceAfter:kCMTimeZero];
406-
dispatch_async(dispatch_get_main_queue(), ^{
407-
[self notifyIfFrameAvailable];
408-
});
406+
[self notifyIfFrameAvailable];
409407
}
410408

411409
- (void)setIsLooping:(bool)isLooping {
@@ -441,11 +439,45 @@ - (void)setPlaybackSpeed:(double)speed {
441439
}
442440

443441
- (CVPixelBufferRef)copyPixelBuffer {
444-
if (_frame == NULL) {
445-
NSLog(@"Returning NULL from copyPixelBuffer");
442+
// Creates a memcpy of the last valid frame.
443+
//
444+
// Unlike on iOS, the macOS embedder does show the last frame when
445+
// we return NULL from `copyPixelBuffer`.
446+
447+
if (_lastValidFrame == NULL) {
448+
return NULL;
449+
}
450+
451+
@synchronized (self) {
452+
CVPixelBufferLockBaseAddress(_lastValidFrame, kCVPixelBufferLock_ReadOnly);
453+
int bufferWidth = (int)CVPixelBufferGetWidth(_lastValidFrame);
454+
int bufferHeight = (int)CVPixelBufferGetHeight(_lastValidFrame);
455+
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(_lastValidFrame);
456+
uint8_t *baseAddress = CVPixelBufferGetBaseAddress(_lastValidFrame);
457+
458+
if (baseAddress == NULL) {
459+
NSLog(@"----> baseadress is NULL");
460+
return NULL;
461+
}
462+
463+
NSDictionary* pixBuffAttributes = @{
464+
(id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA),
465+
(id)kCVPixelBufferIOSurfacePropertiesKey : @{},
466+
(id)kCVPixelBufferOpenGLCompatibilityKey : @YES,
467+
(id)kCVPixelBufferMetalCompatibilityKey : @YES,
468+
};
469+
470+
CVPixelBufferRef pixelBufferCopy = NULL;
471+
CVPixelBufferCreate(kCFAllocatorDefault, bufferWidth, bufferHeight,kCVPixelFormatType_32BGRA,
472+
CFBridgingRetain(pixBuffAttributes), &pixelBufferCopy);
473+
CVPixelBufferLockBaseAddress(pixelBufferCopy, 0);
474+
uint8_t *copyBaseAddress = CVPixelBufferGetBaseAddress(pixelBufferCopy);
475+
memcpy(copyBaseAddress, baseAddress, bufferHeight * bytesPerRow);
476+
477+
CVPixelBufferUnlockBaseAddress(_lastValidFrame, kCVPixelBufferLock_ReadOnly);
478+
CVPixelBufferUnlockBaseAddress(pixelBufferCopy, 0);
479+
return pixelBufferCopy;
446480
}
447-
CVBufferRetain(_frame);
448-
return _frame;
449481
}
450482

451483
- (void)onTextureUnregistered:(NSObject<FlutterTexture>*)texture {
@@ -476,7 +508,7 @@ - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
476508
/// so the channel is going to die or is already dead.
477509
- (void)disposeSansEventChannel {
478510
_disposed = true;
479-
CVBufferRelease(_frame);
511+
CVBufferRelease(_lastValidFrame);
480512
[self stopDisplayLink];
481513
[[_player currentItem] removeObserver:self forKeyPath:@"status" context:statusContext];
482514
[[_player currentItem] removeObserver:self

0 commit comments

Comments
 (0)