feat: Draw onto Frame
as if it was a Skia Canvas (#1479)
* Create Shaders.ts * Add `previewType` and `enableFpsGraph` * Add RN Skia native dependency * Add Skia Preview View on iOS * Pass 1 * Update FrameHostObject.mm * Wrap Canvas * Lockfiles * fix: Fix stuff * chore: Upgrade RNWorklets * Add `previewType` to set the Preview * feat: Add Example * Update project.pbxproj * `enableFpsGraph` * Cache the `std::shared_ptr<FrameHostObject>` * Update CameraView+RecordVideo.swift * Update SkiaMetalCanvasProvider.mm * Android: Integrate Skia Dependency * fix: Use new Prefix * Add example for rendering shader * chore: Upgrade CameraX * Remove KTX * Enable `viewBinding` * Revert "Enable `viewBinding`" This reverts commit f2a603f53b33ea4311a296422ffd1a910ce03f9e. * Revert "chore: Upgrade CameraX" This reverts commit 8dc832cf8754490d31a6192e6c1a1f11cdcd94fe. * Remove unneeded `ProcessCameraProvider.getInstance()` call * fix: Add REA hotfix patch * fix: Fix FrameHostObject dead in runAsync * fix: Make `runAsync` run truly async by dropping new Frames while executing * chore: Upgrade RN Worklets to latest * chore: Upgrade RN Skia * Revert "Remove KTX" This reverts commit 253f586633f7af2da992d2279fc206dc62597129. * Make Skia optional in CMake * Fix import * Update CMakeLists.txt * Update build.gradle * Update CameraView.kt * Update CameraView.kt * Update CameraView.kt * Update Shaders.ts * Center Blur * chore: Upgrade RN Worklets * feat: Add `toByteArray()`, `orientation`, `isMirrored` and `timestamp` to `Frame` (#1487) * feat: Implement `orientation` and `isMirrored` on Frame * feat: Add `toArrayBuffer()` func * perf: Do faster buffer copy * feat: Implement `toArrayBuffer()` on Android * feat: Add `orientation` and `isMirrored` to Android * feat: Add `timestamp` to Frame * Update Frame.ts * Update JImageProxy.h * Update FrameHostObject.cpp * Update FrameHostObject.cpp * Update CameraPage.tsx * fix: Format Swift
This commit is contained in:
@@ -26,7 +26,8 @@ private let propsThatRequireReconfiguration = ["cameraId",
|
||||
"preset",
|
||||
"photo",
|
||||
"video",
|
||||
"enableFrameProcessor"]
|
||||
"enableFrameProcessor",
|
||||
"previewType"]
|
||||
private let propsThatRequireDeviceReconfiguration = ["fps",
|
||||
"hdr",
|
||||
"lowLightBoost",
|
||||
@@ -60,7 +61,9 @@ public final class CameraView: UIView {
|
||||
@objc var isActive = false
|
||||
@objc var torch = "off"
|
||||
@objc var zoom: NSNumber = 1.0 // in "factor"
|
||||
@objc var enableFpsGraph = false
|
||||
@objc var videoStabilizationMode: NSString?
|
||||
@objc var previewType: NSString?
|
||||
// events
|
||||
@objc var onInitialized: RCTDirectEventBlock?
|
||||
@objc var onError: RCTDirectEventBlock?
|
||||
@@ -92,7 +95,6 @@ public final class CameraView: UIView {
|
||||
internal var isRecording = false
|
||||
internal var recordingSession: RecordingSession?
|
||||
@objc public var frameProcessorCallback: FrameProcessorCallback?
|
||||
internal var lastFrameProcessorCall = DispatchTime.now().uptimeNanoseconds
|
||||
// CameraView+TakePhoto
|
||||
internal var photoCaptureDelegates: [PhotoCaptureDelegate] = []
|
||||
// CameraView+Zoom
|
||||
@@ -103,27 +105,19 @@ public final class CameraView: UIView {
|
||||
internal let videoQueue = CameraQueues.videoQueue
|
||||
internal let audioQueue = CameraQueues.audioQueue
|
||||
|
||||
internal var previewView: UIView?
|
||||
#if DEBUG
|
||||
internal var fpsGraph: RCTFPSGraph?
|
||||
#endif
|
||||
|
||||
/// Returns whether the AVCaptureSession is currently running (reflected by isActive)
|
||||
var isRunning: Bool {
|
||||
return captureSession.isRunning
|
||||
}
|
||||
|
||||
/// Convenience wrapper to get layer as its statically known type.
|
||||
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
|
||||
// swiftlint:disable force_cast
|
||||
return layer as! AVCaptureVideoPreviewLayer
|
||||
}
|
||||
|
||||
override public class var layerClass: AnyClass {
|
||||
return AVCaptureVideoPreviewLayer.self
|
||||
}
|
||||
|
||||
// pragma MARK: Setup
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
videoPreviewLayer.session = captureSession
|
||||
videoPreviewLayer.videoGravity = .resizeAspectFill
|
||||
videoPreviewLayer.frame = layer.bounds
|
||||
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(sessionRuntimeError),
|
||||
@@ -141,6 +135,8 @@ public final class CameraView: UIView {
|
||||
selector: #selector(onOrientationChanged),
|
||||
name: UIDevice.orientationDidChangeNotification,
|
||||
object: nil)
|
||||
|
||||
setupPreviewView()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
@@ -165,15 +161,55 @@ public final class CameraView: UIView {
|
||||
|
||||
override public func willMove(toSuperview newSuperview: UIView?) {
|
||||
super.willMove(toSuperview: newSuperview)
|
||||
if !isMounted {
|
||||
isMounted = true
|
||||
guard let onViewReady = onViewReady else {
|
||||
return
|
||||
|
||||
if newSuperview != nil {
|
||||
if !isMounted {
|
||||
isMounted = true
|
||||
guard let onViewReady = onViewReady else {
|
||||
return
|
||||
}
|
||||
onViewReady(nil)
|
||||
}
|
||||
onViewReady(nil)
|
||||
}
|
||||
}
|
||||
|
||||
override public func layoutSubviews() {
|
||||
if let previewView = previewView {
|
||||
previewView.frame = frame
|
||||
previewView.bounds = bounds
|
||||
}
|
||||
}
|
||||
|
||||
func setupPreviewView() {
|
||||
if previewType == "skia" {
|
||||
// Skia Preview View allows user to draw onto a Frame in a Frame Processor
|
||||
if previewView is PreviewSkiaView { return }
|
||||
previewView?.removeFromSuperview()
|
||||
previewView = PreviewSkiaView(frame: frame)
|
||||
} else {
|
||||
// Normal iOS PreviewView is lighter and more performant (YUV Format, GPU only)
|
||||
if previewView is PreviewView { return }
|
||||
previewView?.removeFromSuperview()
|
||||
previewView = PreviewView(frame: frame, session: captureSession)
|
||||
}
|
||||
|
||||
addSubview(previewView!)
|
||||
}
|
||||
|
||||
func setupFpsGraph() {
|
||||
#if DEBUG
|
||||
if enableFpsGraph {
|
||||
if fpsGraph != nil { return }
|
||||
fpsGraph = RCTFPSGraph(frame: CGRect(x: 10, y: 54, width: 75, height: 45), color: .red)
|
||||
fpsGraph!.layer.zPosition = 9999.0
|
||||
addSubview(fpsGraph!)
|
||||
} else {
|
||||
fpsGraph?.removeFromSuperview()
|
||||
fpsGraph = nil
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// pragma MARK: Props updating
|
||||
override public final func didSetProps(_ changedProps: [String]!) {
|
||||
ReactLogger.log(level: .info, message: "Updating \(changedProps.count) prop(s)...")
|
||||
@@ -188,7 +224,18 @@ public final class CameraView: UIView {
|
||||
let shouldUpdateTorch = willReconfigure || changedProps.contains("torch") || shouldCheckActive
|
||||
let shouldUpdateZoom = willReconfigure || changedProps.contains("zoom") || shouldCheckActive
|
||||
let shouldUpdateVideoStabilization = willReconfigure || changedProps.contains("videoStabilizationMode")
|
||||
let shouldUpdateOrientation = changedProps.contains("orientation")
|
||||
let shouldUpdateOrientation = willReconfigure || changedProps.contains("orientation")
|
||||
|
||||
if changedProps.contains("previewType") {
|
||||
DispatchQueue.main.async {
|
||||
self.setupPreviewView()
|
||||
}
|
||||
}
|
||||
if changedProps.contains("enableFpsGraph") {
|
||||
DispatchQueue.main.async {
|
||||
self.setupFpsGraph()
|
||||
}
|
||||
}
|
||||
|
||||
if shouldReconfigure ||
|
||||
shouldReconfigureAudioSession ||
|
||||
@@ -199,6 +246,7 @@ public final class CameraView: UIView {
|
||||
shouldReconfigureDevice ||
|
||||
shouldUpdateVideoStabilization ||
|
||||
shouldUpdateOrientation {
|
||||
// Video Configuration
|
||||
cameraQueue.async {
|
||||
if shouldReconfigure {
|
||||
self.configureCaptureSession()
|
||||
|
Reference in New Issue
Block a user