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

Commit

Permalink
Return a copy of the last valid frame
Browse files Browse the repository at this point in the history
  • Loading branch information
cbenhagen committed Nov 12, 2021
1 parent 9e9aec7 commit 5ad191a
Showing 1 changed file with 32 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ @interface FLTVideoPlayer : NSObject <FlutterTexture, FlutterStreamHandler>
@property(readonly, nonatomic) AVPlayerItemVideoOutput* videoOutput;
@property(readonly, nonatomic) CVDisplayLinkRef displayLink;
@property(readonly, nonatomic) FLTFrameUpdater* frameUpdater;
@property(readonly, nonatomic) CVPixelBufferRef _Nullable frame;
@property(readonly, nonatomic) CVPixelBufferRef _Nullable lastValidFrame;
@property(nonatomic) FlutterEventChannel* eventChannel;
@property(nonatomic) FlutterEventSink eventSink;
@property(nonatomic) CGAffineTransform preferredTransform;
Expand Down Expand Up @@ -170,10 +170,10 @@ - (void)notifyIfFrameAvailable {
if (!_playerItem || _playerItem.status != AVPlayerItemStatusReadyToPlay || ![_videoOutput hasNewPixelBufferForItemTime:outputItemTime]) {
return;
} else {
CVBufferRelease(_frame);
_frame = [_videoOutput copyPixelBufferForItemTime:outputItemTime itemTimeForDisplay:NULL];
if (_frame == NULL) {
NSLog(@"copyPixelBufferForItemTime returned NULL");
CVPixelBufferRef pixelBuffer = [_videoOutput copyPixelBufferForItemTime:outputItemTime itemTimeForDisplay:NULL];
if (pixelBuffer != NULL) {
CVBufferRelease(_lastValidFrame);
_lastValidFrame = pixelBuffer;
}
[_frameUpdater notifyFrameAvailable];
}
Expand Down Expand Up @@ -441,11 +441,32 @@ - (void)setPlaybackSpeed:(double)speed {
}

- (CVPixelBufferRef)copyPixelBuffer {
if (_frame == NULL) {
NSLog(@"Returning NULL from copyPixelBuffer");
}
CVBufferRetain(_frame);
return _frame;
// Creates a memcpy of the last valid frame.
//
// Unlike on iOS, the macOS embedder does show the last frame when
// we return NULL from `copyPixelBuffer`.
CVPixelBufferLockBaseAddress(_lastValidFrame, 0);
int bufferWidth = (int)CVPixelBufferGetWidth(_lastValidFrame);
int bufferHeight = (int)CVPixelBufferGetHeight(_lastValidFrame);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(_lastValidFrame);
uint8_t *baseAddress = CVPixelBufferGetBaseAddress(_lastValidFrame);
NSDictionary* pixBuffAttributes = @{
(id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferIOSurfacePropertiesKey : @{},
(id)kCVPixelBufferOpenGLCompatibilityKey : @YES,
(id)kCVPixelBufferMetalCompatibilityKey : @YES,
};

CVPixelBufferRef pixelBufferCopy = NULL;
CVPixelBufferCreate(kCFAllocatorDefault, bufferWidth, bufferHeight,kCVPixelFormatType_32BGRA,
CFBridgingRetain(pixBuffAttributes), &pixelBufferCopy);
CVPixelBufferLockBaseAddress(pixelBufferCopy, 0);
uint8_t *copyBaseAddress = CVPixelBufferGetBaseAddress(pixelBufferCopy);
memcpy(copyBaseAddress, baseAddress, bufferHeight * bytesPerRow);

CVPixelBufferUnlockBaseAddress(_lastValidFrame, 0);
CVPixelBufferUnlockBaseAddress(pixelBufferCopy, 0);
return pixelBufferCopy;
}

- (void)onTextureUnregistered:(NSObject<FlutterTexture>*)texture {
Expand Down Expand Up @@ -476,7 +497,7 @@ - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
/// so the channel is going to die or is already dead.
- (void)disposeSansEventChannel {
_disposed = true;
CVBufferRelease(_frame);
CVBufferRelease(_lastValidFrame);
[self stopDisplayLink];
[[_player currentItem] removeObserver:self forKeyPath:@"status" context:statusContext];
[[_player currentItem] removeObserver:self
Expand Down

0 comments on commit 5ad191a

Please sign in to comment.