react-native-vision-camera/ios/CameraViewManager.swift

169 lines
6.3 KiB
Swift
Raw Normal View History

2021-02-19 08:28:05 -07:00
//
// CameraViewManager.swift
// Cuvent
//
// Created by Marc Rousavy on 09.11.20.
// Copyright © 2020 Facebook. All rights reserved.
//
import AVFoundation
import Foundation
@objc(CameraViewManager)
final class CameraViewManager: RCTViewManager {
2021-03-26 09:28:08 -06:00
override var methodQueue: DispatchQueue! {
return DispatchQueue.main
}
2021-02-19 08:28:05 -07:00
override static func requiresMainQueueSetup() -> Bool {
return true
}
2021-03-09 02:53:29 -07:00
// pragma MARK: Setup
override final func view() -> UIView! {
return CameraView()
2021-02-19 08:28:05 -07:00
}
// pragma MARK: Exported Functions
@objc
final func startRecording(_ node: NSNumber, options: NSDictionary, onRecordCallback: @escaping RCTResponseSenderBlock) {
2021-02-25 06:07:46 -07:00
let component = getCameraView(withTag: node)
2021-02-19 08:28:05 -07:00
component.startRecording(options: options, callback: onRecordCallback)
}
@objc
final func stopRecording(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
2021-02-25 06:07:46 -07:00
let component = getCameraView(withTag: node)
2021-02-19 08:28:05 -07:00
component.stopRecording(promise: Promise(resolver: resolve, rejecter: reject))
}
2021-02-25 05:59:50 -07:00
2021-02-19 08:28:05 -07:00
@objc
final func takePhoto(_ node: NSNumber, options: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
2021-02-25 06:07:46 -07:00
let component = getCameraView(withTag: node)
2021-02-19 08:28:05 -07:00
component.takePhoto(options: options, promise: Promise(resolver: resolve, rejecter: reject))
}
2021-02-25 05:59:50 -07:00
2021-02-19 08:28:05 -07:00
@objc
final func focus(_ node: NSNumber, point: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
let promise = Promise(resolver: resolve, rejecter: reject)
guard let x = point["x"] as? NSNumber, let y = point["y"] as? NSNumber else {
return promise.reject(error: .parameter(.invalid(unionName: "point", receivedValue: point.description)))
}
2021-02-25 06:07:46 -07:00
let component = getCameraView(withTag: node)
2021-02-19 08:28:05 -07:00
component.focus(point: CGPoint(x: x.doubleValue, y: y.doubleValue), promise: promise)
}
@objc
final func getAvailableVideoCodecs(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
withPromise(resolve: resolve, reject: reject) {
2021-02-25 06:07:46 -07:00
let component = getCameraView(withTag: node)
2021-02-19 08:28:05 -07:00
guard let movieOutput = component.movieOutput else {
throw CameraError.session(SessionError.cameraNotReady)
}
2021-03-09 02:53:29 -07:00
return movieOutput.availableVideoCodecTypes.map(\.descriptor)
2021-02-19 08:28:05 -07:00
}
}
@objc
final func getAvailablePhotoCodecs(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
withPromise(resolve: resolve, reject: reject) {
2021-02-25 06:07:46 -07:00
let component = getCameraView(withTag: node)
2021-02-19 08:28:05 -07:00
guard let photoOutput = component.photoOutput else {
throw CameraError.session(SessionError.cameraNotReady)
}
2021-03-09 02:53:29 -07:00
return photoOutput.availablePhotoCodecTypes.map(\.descriptor)
2021-02-19 08:28:05 -07:00
}
}
// pragma MARK: View Manager funcs
@objc
final func getAvailableCameraDevices(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
withPromise(resolve: resolve, reject: reject) {
let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: getAllDeviceTypes(), mediaType: .video, position: .unspecified)
2021-03-17 08:35:49 -06:00
let devices = discoverySession.devices.filter {
if #available(iOS 11.1, *) {
// exclude the true-depth camera. The True-Depth camera has YUV and Infrared, can't take photos!
return $0.deviceType != .builtInTrueDepthCamera
}
return true
2021-03-17 08:35:49 -06:00
}
return devices.map {
2021-02-19 08:28:05 -07:00
return [
"id": $0.uniqueID,
2021-03-09 02:53:29 -07:00
"devices": $0.physicalDevices.map(\.deviceType.descriptor),
2021-02-19 08:28:05 -07:00
"position": $0.position.descriptor,
"name": $0.localizedName,
"hasFlash": $0.hasFlash,
"hasTorch": $0.hasTorch,
"minZoom": $0.minAvailableVideoZoomFactor,
"maxZoom": $0.maxAvailableVideoZoomFactor,
"neutralZoom": $0.neutralZoomPercent,
"isMultiCam": $0.isMultiCam,
"supportsDepthCapture": false, // TODO: supportsDepthCapture
"supportsRawCapture": false, // TODO: supportsRawCapture
"supportsLowLightBoost": $0.isLowLightBoostSupported,
"supportsFocus": $0.isFocusPointOfInterestSupported,
2021-02-19 08:28:05 -07:00
"formats": $0.formats.map { (format) -> [String: Any] in
format.toDictionary()
},
]
}
}
}
@objc
final func getCameraPermissionStatus(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
withPromise(resolve: resolve, reject: reject) {
let status = AVCaptureDevice.authorizationStatus(for: .video)
return status.descriptor
}
}
@objc
final func getMicrophonePermissionStatus(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
withPromise(resolve: resolve, reject: reject) {
let status = AVCaptureDevice.authorizationStatus(for: .audio)
return status.descriptor
}
}
@objc
final func requestCameraPermission(_ resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
AVCaptureDevice.requestAccess(for: .video) { granted in
let result: AVAuthorizationStatus = granted ? .authorized : .denied
resolve(result.descriptor)
}
}
@objc
final func requestMicrophonePermission(_ resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
AVCaptureDevice.requestAccess(for: .audio) { granted in
let result: AVAuthorizationStatus = granted ? .authorized : .denied
resolve(result.descriptor)
}
}
2021-03-09 02:53:29 -07:00
// MARK: Private
2021-03-26 09:28:08 -06:00
private func getCameraView(withTag tag: NSNumber) -> CameraView {
// swiftlint:disable force_cast
return bridge.uiManager.view(forReactTag: tag) as! CameraView
}
2021-03-09 02:53:29 -07:00
private final func getAllDeviceTypes() -> [AVCaptureDevice.DeviceType] {
var deviceTypes: [AVCaptureDevice.DeviceType] = []
if #available(iOS 13.0, *) {
deviceTypes.append(.builtInTripleCamera)
deviceTypes.append(.builtInDualWideCamera)
deviceTypes.append(.builtInUltraWideCamera)
}
if #available(iOS 11.1, *) {
deviceTypes.append(.builtInTrueDepthCamera)
}
deviceTypes.append(.builtInDualCamera)
deviceTypes.append(.builtInWideAngleCamera)
deviceTypes.append(.builtInTelephotoCamera)
return deviceTypes
}
2021-02-19 08:28:05 -07:00
}