Skip to content

Commit

Permalink
feat: Keep one last Video Frame in memory for Snapshot (#2980)
Browse files Browse the repository at this point in the history
* feat: Keep one last Video Frame in memory for Snapshot

* Other way around

* Update CameraView+TakeSnapshot.swift
  • Loading branch information
mrousavy committed Jun 12, 2024
1 parent 1e1694b commit 29abc3d
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 46 deletions.
48 changes: 13 additions & 35 deletions package/ios/React/CameraView+TakeSnapshot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,27 @@ import UIKit

extension CameraView {
func takeSnapshot(options _: NSDictionary, promise: Promise) {
// If video is not enabled, we won't get any buffers in onFrameListeners. abort it.
guard video else {
promise.reject(error: .capture(.videoNotEnabled))
return
}

// If after 3 seconds we still didn't receive a snapshot, we abort it.
CameraQueues.cameraQueue.asyncAfter(deadline: .now() + 3) {
if !promise.didResolve {
promise.reject(error: .capture(.timedOut))
withPromise(promise) {
guard let snapshot = latestVideoFrame else {
throw CameraError.capture(.snapshotFailed)
}
}

// Add a listener to the onFrame callbacks which will get called later if video is enabled.
snapshotOnFrameListeners.append { buffer in
if promise.didResolve {
// capture was already aborted (timed out)
return
guard let imageBuffer = CMSampleBufferGetImageBuffer(snapshot) else {
throw CameraError.capture(.imageDataAccessError)
}

self.onCaptureShutter(shutterType: .snapshot)

guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else {
promise.reject(error: .capture(.imageDataAccessError))
return
}
let ciImage = CIImage(cvPixelBuffer: imageBuffer)
let orientation = self.cameraSession.outputOrientation
let image = UIImage(ciImage: ciImage, scale: 1.0, orientation: orientation.imageOrientation)
do {
let path = try FileUtils.writeUIImageToTempFile(image: image)
promise.resolve([
"path": path.absoluteString,
"width": image.size.width,
"height": image.size.height,
"orientation": orientation.jsValue,
"isMirrored": false,
])
} catch let error as CameraError {
promise.reject(error: error)
} catch {
promise.reject(error: .capture(.unknown(message: "An unknown error occured while capturing the snapshot!")), cause: error as NSError)
}
let path = try FileUtils.writeUIImageToTempFile(image: image)
return [
"path": path.absoluteString,
"width": image.size.width,
"height": image.size.height,
"orientation": orientation.jsValue,
"isMirrored": false,
]
}
}
}
24 changes: 13 additions & 11 deletions package/ios/React/CameraView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,20 +89,23 @@ public final class CameraView: UIView, CameraSessionDelegate, PreviewViewDelegat
}
}

#if VISION_CAMERA_ENABLE_FRAME_PROCESSORS
@objc public var frameProcessor: FrameProcessor?
#endif

// pragma MARK: Internal Properties
var cameraSession = CameraSession()
var previewView: PreviewView?
var isMounted = false
#if VISION_CAMERA_ENABLE_FRAME_PROCESSORS
@objc public var frameProcessor: FrameProcessor?
#endif
private var currentConfigureCall: DispatchTime?
private let fpsSampleCollector = FpsSampleCollector()

// CameraView+Zoom
var pinchGestureRecognizer: UIPinchGestureRecognizer?
var pinchScaleOffset: CGFloat = 1.0
var snapshotOnFrameListeners: [(_: CMSampleBuffer) -> Void] = []
private var currentConfigureCall: DispatchTime?
private let fpsSampleCollector = FpsSampleCollector()

// CameraView+TakeSnapshot
var latestVideoFrame: CMSampleBuffer?

// pragma MARK: Setup

Expand Down Expand Up @@ -354,6 +357,10 @@ public final class CameraView: UIView, CameraSessionDelegate, PreviewViewDelegat
}

func onFrame(sampleBuffer: CMSampleBuffer, orientation: Orientation) {
// Update latest frame that can be used for snapshot capture
latestVideoFrame = sampleBuffer

// Notify FPS Collector that we just had a Frame
fpsSampleCollector.onTick()

#if VISION_CAMERA_ENABLE_FRAME_PROCESSORS
Expand All @@ -363,11 +370,6 @@ public final class CameraView: UIView, CameraSessionDelegate, PreviewViewDelegat
frameProcessor.call(frame)
}
#endif

for callback in snapshotOnFrameListeners {
callback(sampleBuffer)
}
snapshotOnFrameListeners.removeAll()
}

func onCodeScanned(codes: [CameraSession.Code], scannerFrame: CameraSession.CodeScannerFrame) {
Expand Down

0 comments on commit 29abc3d

Please sign in to comment.