capacitor-camera-view 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapacitorCameraView.podspec +17 -0
- package/LICENSE +201 -0
- package/Package.swift +28 -0
- package/README.md +654 -0
- package/android/build.gradle +79 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraView.kt +555 -0
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraViewPlugin.kt +227 -0
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/BarcodeDetectionResult.kt +11 -0
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/CameraDevice.kt +14 -0
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/CameraSessionConfiguration.kt +10 -0
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/WebBoundingRect.kt +16 -0
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/ZoomFactors.kt +14 -0
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/utils.kt +86 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +968 -0
- package/dist/esm/definitions.d.ts +378 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/utils.d.ts +45 -0
- package/dist/esm/utils.js +108 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/esm/web.d.ts +108 -0
- package/dist/esm/web.js +406 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +530 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +533 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/CameraViewPlugin/CameraError.swift +39 -0
- package/ios/Sources/CameraViewPlugin/CameraSessionConfiguration.swift +32 -0
- package/ios/Sources/CameraViewPlugin/CameraViewManager+BarcodeScan.swift +91 -0
- package/ios/Sources/CameraViewPlugin/CameraViewManager+PhotoCapture.swift +52 -0
- package/ios/Sources/CameraViewPlugin/CameraViewManager+VideoDataOutput.swift +78 -0
- package/ios/Sources/CameraViewPlugin/CameraViewManager.swift +633 -0
- package/ios/Sources/CameraViewPlugin/CameraViewPlugin.swift +295 -0
- package/ios/Sources/CameraViewPlugin/Utils.swift +56 -0
- package/ios/Tests/CameraViewPluginTests/CameraViewPluginTests.swift +15 -0
- package/package.json +94 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import AVFoundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
public struct CameraSessionConfiguration {
|
|
5
|
+
let deviceId: String?
|
|
6
|
+
let enableBarcodeDetection: Bool
|
|
7
|
+
let position: AVCaptureDevice.Position
|
|
8
|
+
let preferredCameraDeviceTypes: [String]?
|
|
9
|
+
let useTripleCameraIfAvailable: Bool
|
|
10
|
+
let zoomFactor: CGFloat?
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/// Maps a Capacitor plugin call to a CameraSessionConfiguration struct.
|
|
14
|
+
///
|
|
15
|
+
/// - Parameter call: The Capacitor plugin call.
|
|
16
|
+
public func sessionConfigFromPluginCall(_ call: CAPPluginCall) -> CameraSessionConfiguration {
|
|
17
|
+
let deviceId = call.getString("deviceId")
|
|
18
|
+
let enableBarcodeDetection = call.getBool("enableBarcodeDetection", false)
|
|
19
|
+
let position: AVCaptureDevice.Position = call.getString("position") == "front" ? .front : .back
|
|
20
|
+
let preferredCameraDeviceTypes = call.getArray("preferredCameraDeviceTypes") as? [String]
|
|
21
|
+
let useTripleCameraIfAvailable = call.getBool("useTripleCameraIfAvailable", false)
|
|
22
|
+
let zoomFactor = call.getDouble("zoomFactor").map { CGFloat($0) }
|
|
23
|
+
|
|
24
|
+
return CameraSessionConfiguration(
|
|
25
|
+
deviceId: deviceId,
|
|
26
|
+
enableBarcodeDetection: enableBarcodeDetection,
|
|
27
|
+
position: position,
|
|
28
|
+
preferredCameraDeviceTypes: preferredCameraDeviceTypes,
|
|
29
|
+
useTripleCameraIfAvailable: useTripleCameraIfAvailable,
|
|
30
|
+
zoomFactor: zoomFactor
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import AVFoundation
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
extension CameraViewManager: AVCaptureMetadataOutputObjectsDelegate {
|
|
5
|
+
/// Set up metadata output for the capture session in case it's not configured yet
|
|
6
|
+
/// Make sure to call `captureSession.beginConfiguration` before calling this
|
|
7
|
+
///
|
|
8
|
+
/// - Throws: An error if the output cannot be set.
|
|
9
|
+
internal func setupMetadataOutput() throws {
|
|
10
|
+
let metadataOutput = AVCaptureMetadataOutput()
|
|
11
|
+
|
|
12
|
+
if (captureSession.outputs.contains { $0 is AVCaptureMetadataOutput }) {
|
|
13
|
+
// Nothing todo, we already have an output
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if !captureSession.canAddOutput(metadataOutput) {
|
|
18
|
+
throw CameraError.outputAdditionFailed
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
captureSession.addOutput(metadataOutput)
|
|
22
|
+
|
|
23
|
+
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
|
|
24
|
+
|
|
25
|
+
// Set all available barcode types
|
|
26
|
+
metadataOutput.metadataObjectTypes = [
|
|
27
|
+
.qr,
|
|
28
|
+
.code128,
|
|
29
|
+
.code39,
|
|
30
|
+
.code39Mod43,
|
|
31
|
+
.code93,
|
|
32
|
+
.ean8,
|
|
33
|
+
.ean13,
|
|
34
|
+
.interleaved2of5,
|
|
35
|
+
.itf14,
|
|
36
|
+
.pdf417,
|
|
37
|
+
.aztec,
|
|
38
|
+
.dataMatrix,
|
|
39
|
+
.upce,
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Delegate method called when metadata objects are detected in the camera feed
|
|
44
|
+
///
|
|
45
|
+
/// This method processes barcode data detected in the camera stream. When a barcode is detected,
|
|
46
|
+
/// it extracts the barcode value and type, then emits a Capacitor event with the barcode data.
|
|
47
|
+
///
|
|
48
|
+
/// - Parameters:
|
|
49
|
+
/// - output: The metadata output object that captured the data.
|
|
50
|
+
/// - metadataObjects: An array of detected metadata objects, potentially including barcodes.
|
|
51
|
+
/// - connection: The connection through which the metadata objects were captured.
|
|
52
|
+
public func metadataOutput(
|
|
53
|
+
_ output: AVCaptureMetadataOutput,
|
|
54
|
+
didOutput metadataObjects: [AVMetadataObject],
|
|
55
|
+
from connection: AVCaptureConnection
|
|
56
|
+
) {
|
|
57
|
+
guard let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject
|
|
58
|
+
else {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
guard let barcodeValue = metadataObject.stringValue else { return }
|
|
62
|
+
|
|
63
|
+
let barcodeType = metadataObject.type.rawValue
|
|
64
|
+
|
|
65
|
+
// Transform the metadata object to the coordinate space of the video preview layer
|
|
66
|
+
// This is necessary to get the correct bounding box for the detected barcode
|
|
67
|
+
// Which in our case should always equal to the device's screen
|
|
68
|
+
// This way we can simply use pixel coordinates to get the bounding box of the detected barcode and easily show it in the webview
|
|
69
|
+
guard let transformedMetadataObject = videoPreviewLayer.transformedMetadataObject(for: metadataObject)
|
|
70
|
+
else {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let boundingRect: [String: Double] = [
|
|
75
|
+
"x": Double(transformedMetadataObject.bounds.origin.x),
|
|
76
|
+
"y": Double(transformedMetadataObject.bounds.origin.y),
|
|
77
|
+
"width": Double(transformedMetadataObject.bounds.width),
|
|
78
|
+
"height": Double(transformedMetadataObject.bounds.height),
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
NotificationCenter.default.post(
|
|
82
|
+
name: Notification.Name("barcodeDetected"),
|
|
83
|
+
object: nil,
|
|
84
|
+
userInfo: [
|
|
85
|
+
"value": barcodeValue,
|
|
86
|
+
"type": barcodeType,
|
|
87
|
+
"boundingRect": boundingRect,
|
|
88
|
+
]
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import AVFoundation
|
|
2
|
+
import Foundation
|
|
3
|
+
import UIKit
|
|
4
|
+
|
|
5
|
+
extension CameraViewManager: AVCapturePhotoCaptureDelegate {
|
|
6
|
+
/// Set up output for the capture session in case it's not configured yet
|
|
7
|
+
/// Make sure to call `captureSession.beginConfiguration` before calling this
|
|
8
|
+
///
|
|
9
|
+
/// - Throws: An error if the output cannot be set.
|
|
10
|
+
internal func setupPhotoOutput() throws {
|
|
11
|
+
if (captureSession.outputs.contains { $0 is AVCapturePhotoOutput }) {
|
|
12
|
+
// Nothing todo, we already have an output and since we only
|
|
13
|
+
// use outputs for taking photos here we don't need a new one
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Balanced should be a good choice for most use cases
|
|
18
|
+
avPhotoOutput.maxPhotoQualityPrioritization = .balanced
|
|
19
|
+
|
|
20
|
+
if !captureSession.canAddOutput(avPhotoOutput) {
|
|
21
|
+
throw CameraError.outputAdditionFailed
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
captureSession.addOutput(avPhotoOutput)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Delegate method called when a photo has been captured via `AVCapturePhotoCaptureDelegate`
|
|
28
|
+
///
|
|
29
|
+
/// - Parameters:
|
|
30
|
+
/// - output: The photo output that captured the photo.
|
|
31
|
+
/// - photo: The captured photo.
|
|
32
|
+
/// - error: An error that occurred during photo capture.
|
|
33
|
+
public func photoOutput(
|
|
34
|
+
_ output: AVCapturePhotoOutput,
|
|
35
|
+
didFinishProcessingPhoto photo: AVCapturePhoto,
|
|
36
|
+
error: Error?
|
|
37
|
+
) {
|
|
38
|
+
if let error = error {
|
|
39
|
+
photoCaptureHandler?(nil, error)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
guard let data = photo.fileDataRepresentation(), let image = UIImage(data: data) else {
|
|
44
|
+
photoCaptureHandler?(nil, CameraError.photoOutputError)
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
photoCaptureHandler?(image, nil)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import AVFoundation
|
|
2
|
+
import CoreImage
|
|
3
|
+
import Foundation
|
|
4
|
+
import UIKit
|
|
5
|
+
|
|
6
|
+
extension CameraViewManager: AVCaptureVideoDataOutputSampleBufferDelegate {
|
|
7
|
+
/// Set up video data output for the capture session in case it's not configured yet
|
|
8
|
+
/// This is used for taking snapshots of the camera feed
|
|
9
|
+
/// Make sure to call `captureSession.beginConfiguration` before calling this
|
|
10
|
+
///
|
|
11
|
+
/// - Throws: An error if the output cannot be set.
|
|
12
|
+
/// - Note: This method does not set the delegate for the output. The delegate is set
|
|
13
|
+
/// when a snapshot is requested.
|
|
14
|
+
internal func setupVideoDataOutput() throws {
|
|
15
|
+
if (captureSession.outputs.contains { $0 is AVCaptureVideoDataOutput }) {
|
|
16
|
+
// Nothing todo, we already have an output and since we only
|
|
17
|
+
// use video outputs for taking snapshots here we don't need a new one
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Configure video data output
|
|
22
|
+
avVideoDataOutput.videoSettings = [
|
|
23
|
+
kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)
|
|
24
|
+
]
|
|
25
|
+
avVideoDataOutput.alwaysDiscardsLateVideoFrames = true
|
|
26
|
+
|
|
27
|
+
// We're not setting the delegate here as we'll set it only when needed for snapshot capture
|
|
28
|
+
|
|
29
|
+
if !captureSession.canAddOutput(avVideoDataOutput) {
|
|
30
|
+
throw CameraError.outputAdditionFailed
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
captureSession.addOutput(avVideoDataOutput)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Capture a snapshot from the camera feed
|
|
37
|
+
public func captureOutput(
|
|
38
|
+
_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer,
|
|
39
|
+
from connection: AVCaptureConnection
|
|
40
|
+
) {
|
|
41
|
+
// Only process if we have a completion handler set
|
|
42
|
+
guard let completionHandler = snapshotCompletionHandler else { return }
|
|
43
|
+
|
|
44
|
+
// Clear the completion handler to ensure we only capture one frame
|
|
45
|
+
snapshotCompletionHandler = nil
|
|
46
|
+
|
|
47
|
+
avVideoDataOutput.setSampleBufferDelegate(nil, queue: nil)
|
|
48
|
+
|
|
49
|
+
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
|
|
50
|
+
completionHandler(nil, CameraError.frameCaptureError)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let ciImage = CIImage(cvPixelBuffer: imageBuffer)
|
|
55
|
+
let context = CIContext()
|
|
56
|
+
guard let cgImage = context.createCGImage(ciImage, from: ciImage.extent) else {
|
|
57
|
+
completionHandler(nil, CameraError.frameCaptureError)
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let image = UIImage(cgImage: cgImage)
|
|
62
|
+
completionHandler(image, nil)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Delegate method called when a frame is dropped via `AVCaptureVideoDataOutputSampleBufferDelegate`
|
|
66
|
+
public func captureOutput(
|
|
67
|
+
_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer,
|
|
68
|
+
from connection: AVCaptureConnection
|
|
69
|
+
) {
|
|
70
|
+
// If we have a completion handler and a frame was dropped, report the error
|
|
71
|
+
if let completionHandler = snapshotCompletionHandler {
|
|
72
|
+
snapshotCompletionHandler = nil
|
|
73
|
+
avVideoDataOutput.setSampleBufferDelegate(nil, queue: nil)
|
|
74
|
+
|
|
75
|
+
completionHandler(nil, CameraError.frameCaptureError)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|