I use a Native Camera View from Swift within my React Native app. Within the delegate Methods of my Camera View I check frames etc. I decided to build it as the following:
React Native Main Class of my App:
...
import CamView from './CamView.js';
...
class CamScreen extends Component {
...
render() {
...
return(
...
<CamView style={[styles.camStyle]}/>
...
)
}
...
}
...
And my CamView.js is the following:
import { requireNativeComponent } from "react-native";
module.exports = requireNativeComponent("RCTCameraView");
From which I use my Camera Manager:
...
@objc(RCTCameraViewManager)
class CameraViewManager: RCTViewManager {
var sharedCamView: CamView { return CamView.shared() }
override static func requiresMainQueueSetup() -> Bool { return true }
override static func moduleName() -> String! { return "CameraViewManager" }
override func view() -> UIView! { return sharedCamView }
override func constantsToExport() -> [AnyHashable : Any] { return ["sharedCamView": sharedCamView.reactTag] }
}
And now my Camera View swift code:
@objc(CamView)
class CamView: UIView, RCTBridgeModule, AVCaptureVideoDataOutputSampleBufferDelegate, AVCapturePhotoCaptureDelegate{
...
var isSessionRunning = false
...
override init(frame: CGRect) {
super.init(frame: frame)
self.initializeLibraryFunctions()
self.setupView()
if(self.sessionSetup != .success) {
self.setupSession()
}
}
deinit {
self.session?.stopRunning()
self.isSessionRunning = false
self.videoOutput!.setSampleBufferDelegate(nil, queue: nil)
}
override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
if newSuperview != nil {
self.startSession()
} else {
self.stopSession()
}
}
...
// start session
func startSession() {
if self.isSessionRunning == false && self.sessionSetup == .success {
if self.session!.isRunning == false {
// self.videoOutput!.setSampleBufferDelegate(self, queue: self.captureQueue)
self.session!.startRunning()
self.isSessionRunning = true
}
}
}
// stop session
func stopSession() {
if self.isSessionRunning == true && self.sessionSetup == .success {
if self.session!.isRunning == true {
// self.videoOutput!.setSampleBufferDelegate(nil, queue: nil)
self.session!.stopRunning()
self.isSessionRunning = false
}
}
}
}
@objc
func resetCapture() -> Void{
// I want to print the status here if my session is running
RNEventEmitter.emitter.sendEvent(withName: "myEventName", body:"isSessionRunning: \(self.isSessionRunning)")
}
...
func setupSession() {
if(self.sessionHasBeenSetup == true) { return }
checkPermissions()
self.session = AVCaptureSession()
self.videoPreviewLayer.session = self.session
self.videoPreviewLayer.videoGravity = .resizeAspectFill
self.videoPreviewLayer.frame = self.layer.bounds
self.videoOutput = AVCaptureVideoDataOutput()
self.photoOutput = AVCapturePhotoOutput()
self.session!.beginConfiguration()
// set up device
guard let device = AVCaptureDevice.default(for: .video) else {
self.sessionSetup = .failure
self.session!.commitConfiguration()
return
}
if device.isFocusModeSupported(.continuousAutoFocus) {
try! device.lockForConfiguration()
device.focusMode = .continuousAutoFocus
device.unlockForConfiguration()
}
// set up input
do {
let input = try AVCaptureDeviceInput(device: device)
if self.session!.canAddInput(input) {
self.session!.addInput(input)
} else {
self.sessionSetup = .failure
self.session!.commitConfiguration()
return
}
} catch {
self.sessionSetup = .failure
self.session!.commitConfiguration()
return
}
// set video output
// "com.example.CamViewQueue" is an example for my actual queue which is the same as the queue when I check from resetCapture
let myQueue = DispatchQueue(label: "com.example.CamViewQueue", attributes: DispatchQueue.Attributes.concurrent)
self.videoOutput!.setSampleBufferDelegate(self, queue: myQueue)
if self.session!.canAddOutput(self.videoOutput!) {
self.session!.addOutput(self.videoOutput!)
} else {
self.sessionSetup = .failure
self.session!.commitConfiguration()
return
}
// set up photo output
if self.session!.canSetSessionPreset(.photo) { session!.sessionPreset = .photo }
if self.session!.canAddOutput(self.photoOutput!) {
self.session!.addOutput(self.photoOutput!)
} else {
self.sessionSetup = .failure
self.session!.commitConfiguration()
return
}
self.session!.commitConfiguration()
self.sessionSetup = .success
}
...
// Delegate Methods
public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
if captureInProgress == false && processingImage == false {
// printing thread name results in the same as in resetCapture: Thread.current.threadName
// printing isSessionRunning here results in true while in resetCapture it shows false
RNEventEmitter.emitter.sendEvent(withName: "myEventName", body:"isSessionRunning: \(self.isSessionRunning)")
processingImage = true // block for entering
let group = DispatchGroup()
group.enter()
DispatchQueue.main.async{
patternStatus = self.myProcessingFunction(sampleBuffer: sampleBuffer)
group.leave()
}
group.wait()
group.notify(queue: .main) {
// do stuff here
} else {
self.processingImage = false
}
}
}
}
Why does printing isSessionRunning results to true within my delegate but from the outside it is false when I invoke resetCapture from my Reactnative App by pushing a button? Both operations are from the same Queue and I think both are from the same CamView instance as far as I understand.
Also when I compare my sessions
self.videoPreviewLayer.session == self.session
within my resetCapture
function both are equal. What I want to do with my resetCapture
is to start (and if possible stop and then start) my session. But if I forcefully use self.session.stopRunning()
the CamView freezes (but the delegate output gets printed).