@capgo/camera-preview 8.4.8 → 8.5.1
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/README.md +157 -6
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +131 -3
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java +149 -15
- package/android/src/main/java/app/capgo/capacitor/camera/preview/model/CameraSessionConfiguration.java +14 -1
- package/dist/docs.json +367 -8
- package/dist/esm/definitions.d.ts +109 -8
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +15 -1
- package/dist/esm/web.js +19 -1
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +19 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +19 -1
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/CameraController.swift +175 -77
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +64 -5
- package/package.json +1 -1
|
@@ -57,6 +57,16 @@ class CameraController: NSObject {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
|
|
61
|
+
private func setVideoOrientation(_ orientation: AVCaptureVideoOrientation, on connection: AVCaptureConnection) {
|
|
62
|
+
guard connection.isVideoOrientationSupported else { return }
|
|
63
|
+
connection.videoOrientation = orientation
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private func setVideoOrientation(_ orientation: AVCaptureVideoOrientation, on connections: [AVCaptureConnection]) {
|
|
67
|
+
connections.forEach { setVideoOrientation(orientation, on: $0) }
|
|
68
|
+
}
|
|
69
|
+
|
|
60
70
|
// Continuous focus with significant movement if focus was locked from setFocus earlier
|
|
61
71
|
@objc private func subjectAreaDidChange(notification: NSNotification) {
|
|
62
72
|
guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
|
|
@@ -140,9 +150,79 @@ class CameraController: NSObject {
|
|
|
140
150
|
private var lastCaptureOrientation: AVCaptureVideoOrientation?
|
|
141
151
|
|
|
142
152
|
var videoFileURL: URL?
|
|
153
|
+
var recordingFinishedCallback: ((URL, String) -> Void)?
|
|
143
154
|
private let saneMaxZoomFactor: CGFloat = 25.5
|
|
144
155
|
|
|
145
156
|
var videoQuality: String = "high"
|
|
157
|
+
var videoCodec: String = "avc1"
|
|
158
|
+
|
|
159
|
+
private static let allVideoQualities = ["low", "medium", "high", "2160p", "1080p", "720p", "480p", "4:3"]
|
|
160
|
+
|
|
161
|
+
func getSupportedVideoQualities() -> [String] {
|
|
162
|
+
guard let captureSession = self.captureSession else {
|
|
163
|
+
return Self.allVideoQualities
|
|
164
|
+
}
|
|
165
|
+
let device = self.currentCameraPosition == .front ? self.frontCamera : self.rearCamera
|
|
166
|
+
guard let device = device else {
|
|
167
|
+
return Self.allVideoQualities
|
|
168
|
+
}
|
|
169
|
+
return Self.allVideoQualities.filter { quality in
|
|
170
|
+
let preset = self.bestPreset(for: self.requestedAspectRatio, quality: quality, on: device)
|
|
171
|
+
return captureSession.canSetSessionPreset(preset)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
func setVideoQuality(_ quality: String) throws {
|
|
176
|
+
guard Self.allVideoQualities.contains(quality.lowercased()) else {
|
|
177
|
+
throw CameraControllerError.invalidOperation
|
|
178
|
+
}
|
|
179
|
+
self.videoQuality = quality.lowercased()
|
|
180
|
+
self.configureSessionPreset(for: self.requestedAspectRatio)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
func getVideoQuality() -> String {
|
|
184
|
+
return self.videoQuality
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
func getSupportedVideoCodecs() -> [String] {
|
|
188
|
+
guard let fileVideoOutput = self.fileVideoOutput else {
|
|
189
|
+
return ["avc1"]
|
|
190
|
+
}
|
|
191
|
+
var codecs: [String] = []
|
|
192
|
+
if fileVideoOutput.availableVideoCodecTypes.contains(.h264) {
|
|
193
|
+
codecs.append("avc1")
|
|
194
|
+
}
|
|
195
|
+
if fileVideoOutput.availableVideoCodecTypes.contains(.hevc) {
|
|
196
|
+
codecs.append("hvc1")
|
|
197
|
+
}
|
|
198
|
+
return codecs.isEmpty ? ["avc1"] : codecs
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
func setVideoCodec(_ codec: String) throws {
|
|
202
|
+
let normalized = codec.lowercased()
|
|
203
|
+
guard ["avc1", "hvc1"].contains(normalized) else {
|
|
204
|
+
throw CameraControllerError.invalidOperation
|
|
205
|
+
}
|
|
206
|
+
if !getSupportedVideoCodecs().contains(normalized) {
|
|
207
|
+
throw CameraControllerError.invalidOperation
|
|
208
|
+
}
|
|
209
|
+
self.videoCodec = normalized
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
func getVideoCodec() -> String {
|
|
213
|
+
return self.videoCodec
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private func avVideoCodecType(for codec: String) -> AVVideoCodecType? {
|
|
217
|
+
switch codec.lowercased() {
|
|
218
|
+
case "avc1":
|
|
219
|
+
return .h264
|
|
220
|
+
case "hvc1":
|
|
221
|
+
return .hevc
|
|
222
|
+
default:
|
|
223
|
+
return nil
|
|
224
|
+
}
|
|
225
|
+
}
|
|
146
226
|
|
|
147
227
|
// Track output preparation status
|
|
148
228
|
private var outputsPrepared: Bool = false
|
|
@@ -346,45 +426,7 @@ extension CameraController {
|
|
|
346
426
|
let layer = AVCaptureVideoPreviewLayer()
|
|
347
427
|
// Configure orientation immediately
|
|
348
428
|
if let connection = layer.connection {
|
|
349
|
-
|
|
350
|
-
if Thread.isMainThread {
|
|
351
|
-
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
|
|
352
|
-
switch windowScene.interfaceOrientation {
|
|
353
|
-
case .portrait:
|
|
354
|
-
connection.videoOrientation = .portrait
|
|
355
|
-
case .landscapeLeft:
|
|
356
|
-
connection.videoOrientation = .landscapeLeft
|
|
357
|
-
case .landscapeRight:
|
|
358
|
-
connection.videoOrientation = .landscapeRight
|
|
359
|
-
case .portraitUpsideDown:
|
|
360
|
-
connection.videoOrientation = .portraitUpsideDown
|
|
361
|
-
case .unknown:
|
|
362
|
-
fallthrough
|
|
363
|
-
@unknown default:
|
|
364
|
-
connection.videoOrientation = .portrait
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
} else {
|
|
368
|
-
// If not on main thread, use a sync call to get the orientation
|
|
369
|
-
DispatchQueue.main.sync {
|
|
370
|
-
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
|
|
371
|
-
switch windowScene.interfaceOrientation {
|
|
372
|
-
case .portrait:
|
|
373
|
-
connection.videoOrientation = .portrait
|
|
374
|
-
case .landscapeLeft:
|
|
375
|
-
connection.videoOrientation = .landscapeLeft
|
|
376
|
-
case .landscapeRight:
|
|
377
|
-
connection.videoOrientation = .landscapeRight
|
|
378
|
-
case .portraitUpsideDown:
|
|
379
|
-
connection.videoOrientation = .portraitUpsideDown
|
|
380
|
-
case .unknown:
|
|
381
|
-
fallthrough
|
|
382
|
-
@unknown default:
|
|
383
|
-
connection.videoOrientation = .portrait
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
429
|
+
self.setVideoOrientation(self.getVideoOrientation(), on: connection)
|
|
388
430
|
}
|
|
389
431
|
// Don't set session here - we'll do it during configuration
|
|
390
432
|
self.previewLayer = layer
|
|
@@ -457,7 +499,7 @@ extension CameraController {
|
|
|
457
499
|
let videoQueue = DispatchQueue(label: "com.camera.videoQueue", qos: .userInteractive)
|
|
458
500
|
dataOutput.setSampleBufferDelegate(self, queue: videoQueue)
|
|
459
501
|
// Set orientation immediately
|
|
460
|
-
|
|
502
|
+
self.setVideoOrientation(videoOrientation, on: dataOutput.connections)
|
|
461
503
|
}
|
|
462
504
|
|
|
463
505
|
// Add photo output immediately to avoid later reconfiguration
|
|
@@ -465,21 +507,21 @@ extension CameraController {
|
|
|
465
507
|
photoOutput.isHighResolutionCaptureEnabled = true
|
|
466
508
|
captureSession.addOutput(photoOutput)
|
|
467
509
|
// Set orientation immediately
|
|
468
|
-
|
|
510
|
+
self.setVideoOrientation(videoOrientation, on: photoOutput.connections)
|
|
469
511
|
}
|
|
470
512
|
|
|
471
513
|
// Add video output if in camera mode
|
|
472
514
|
if cameraMode, let fileVideoOutput = self.fileVideoOutput, captureSession.canAddOutput(fileVideoOutput) {
|
|
473
515
|
captureSession.addOutput(fileVideoOutput)
|
|
474
516
|
// Set orientation immediately
|
|
475
|
-
|
|
517
|
+
self.setVideoOrientation(videoOrientation, on: fileVideoOutput.connections)
|
|
476
518
|
}
|
|
477
519
|
|
|
478
520
|
// Set up preview layer session in the same configuration block
|
|
479
521
|
if let layer = self.previewLayer {
|
|
480
522
|
layer.session = captureSession
|
|
481
523
|
// Set orientation for preview layer
|
|
482
|
-
layer.connection
|
|
524
|
+
if let connection = layer.connection { self.setVideoOrientation(videoOrientation, on: connection) }
|
|
483
525
|
// Start with a very subtle fade to smooth any remaining visual artifacts
|
|
484
526
|
layer.opacity = 0.95
|
|
485
527
|
}
|
|
@@ -528,28 +570,40 @@ extension CameraController {
|
|
|
528
570
|
|
|
529
571
|
// Prioritize video quality setting
|
|
530
572
|
switch self.videoQuality.lowercased() {
|
|
531
|
-
case "
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
573
|
+
case "2160p":
|
|
574
|
+
if captureSession.canSetSessionPreset(.hd4K3840x2160) {
|
|
575
|
+
targetPreset = .hd4K3840x2160
|
|
576
|
+
} else if captureSession.canSetSessionPreset(.hd1920x1080) {
|
|
577
|
+
targetPreset = .hd1920x1080
|
|
578
|
+
}
|
|
579
|
+
case "1080p":
|
|
580
|
+
if captureSession.canSetSessionPreset(.hd1920x1080) {
|
|
581
|
+
targetPreset = .hd1920x1080
|
|
582
|
+
} else if captureSession.canSetSessionPreset(.hd1280x720) {
|
|
583
|
+
targetPreset = .hd1280x720
|
|
537
584
|
}
|
|
538
|
-
case "medium":
|
|
539
|
-
// Match Android "Medium" (HD/720p)
|
|
585
|
+
case "720p", "medium":
|
|
540
586
|
if captureSession.canSetSessionPreset(.hd1280x720) {
|
|
541
587
|
targetPreset = .hd1280x720
|
|
542
588
|
} else {
|
|
543
589
|
targetPreset = .medium
|
|
544
590
|
}
|
|
591
|
+
case "480p", "low":
|
|
592
|
+
if captureSession.canSetSessionPreset(.vga640x480) {
|
|
593
|
+
targetPreset = .vga640x480
|
|
594
|
+
} else {
|
|
595
|
+
targetPreset = .low
|
|
596
|
+
}
|
|
597
|
+
case "4:3":
|
|
598
|
+
if captureSession.canSetSessionPreset(.photo) {
|
|
599
|
+
targetPreset = .photo
|
|
600
|
+
} else if captureSession.canSetSessionPreset(.high) {
|
|
601
|
+
targetPreset = .high
|
|
602
|
+
}
|
|
545
603
|
case "high":
|
|
546
|
-
// Exisiting logic for High Quality (4K/1080p based on Asepct Ratio)
|
|
547
|
-
|
|
548
604
|
if let aspectRatio = aspectRatio {
|
|
549
605
|
switch aspectRatio {
|
|
550
606
|
case "16:9":
|
|
551
|
-
// Start with 1080p for faster initialization, 4K only when explicitly needed
|
|
552
|
-
// This maintains capture quality while optimizing preview performance
|
|
553
607
|
if captureSession.canSetSessionPreset(.hd1920x1080) {
|
|
554
608
|
targetPreset = .hd1920x1080
|
|
555
609
|
} else if captureSession.canSetSessionPreset(.hd4K3840x2160) {
|
|
@@ -573,7 +627,6 @@ extension CameraController {
|
|
|
573
627
|
}
|
|
574
628
|
}
|
|
575
629
|
}
|
|
576
|
-
// Handle unexpected values
|
|
577
630
|
default:
|
|
578
631
|
if captureSession.canSetSessionPreset(.photo) {
|
|
579
632
|
targetPreset = .photo
|
|
@@ -849,9 +902,10 @@ extension CameraController {
|
|
|
849
902
|
// Apply orientation asynchronously on main thread
|
|
850
903
|
let updateBlock = { [weak self] in
|
|
851
904
|
guard let self = self else { return }
|
|
852
|
-
self.previewLayer?.connection
|
|
853
|
-
self.dataOutput?.connections
|
|
854
|
-
self.photoOutput?.connections
|
|
905
|
+
if let connection = self.previewLayer?.connection { self.setVideoOrientation(videoOrientation, on: connection) }
|
|
906
|
+
if let connections = self.dataOutput?.connections { self.setVideoOrientation(videoOrientation, on: connections) }
|
|
907
|
+
if let connections = self.photoOutput?.connections { self.setVideoOrientation(videoOrientation, on: connections) }
|
|
908
|
+
if let connections = self.fileVideoOutput?.connections { self.setVideoOrientation(videoOrientation, on: connections) }
|
|
855
909
|
}
|
|
856
910
|
|
|
857
911
|
if Thread.isMainThread {
|
|
@@ -893,28 +947,37 @@ extension CameraController {
|
|
|
893
947
|
|
|
894
948
|
// Handle specific quality overrides first
|
|
895
949
|
switch quality.lowercased() {
|
|
896
|
-
case "
|
|
897
|
-
if device.supportsSessionPreset(.
|
|
898
|
-
return .
|
|
899
|
-
|
|
950
|
+
case "2160p":
|
|
951
|
+
if device.supportsSessionPreset(.hd4K3840x2160) { return .hd4K3840x2160 }
|
|
952
|
+
if device.supportsSessionPreset(.hd1920x1080) { return .hd1920x1080 }
|
|
953
|
+
if device.supportsSessionPreset(.hd1280x720) { return .hd1280x720 }
|
|
954
|
+
return .vga640x480
|
|
955
|
+
case "1080p":
|
|
956
|
+
if device.supportsSessionPreset(.hd1920x1080) { return .hd1920x1080 }
|
|
957
|
+
if device.supportsSessionPreset(.hd1280x720) { return .hd1280x720 }
|
|
958
|
+
return .vga640x480
|
|
959
|
+
case "720p", "medium":
|
|
900
960
|
if device.supportsSessionPreset(.hd1280x720) { return .hd1280x720 }
|
|
901
961
|
return .medium
|
|
902
|
-
case "
|
|
903
|
-
|
|
962
|
+
case "480p", "low":
|
|
963
|
+
if device.supportsSessionPreset(.vga640x480) { return .vga640x480 }
|
|
964
|
+
return .low
|
|
965
|
+
case "4:3":
|
|
966
|
+
if device.supportsSessionPreset(.photo) { return .photo }
|
|
967
|
+
if device.supportsSessionPreset(.high) { return .high }
|
|
968
|
+
return .vga640x480
|
|
904
969
|
default:
|
|
905
|
-
break
|
|
970
|
+
break
|
|
906
971
|
}
|
|
907
972
|
// Preference order depends on aspect ratio
|
|
908
973
|
if aspectRatio == "16:9" {
|
|
909
|
-
// Prefer 4K → 1080p → 720p → high → photo → vga
|
|
910
974
|
if device.supportsSessionPreset(.hd4K3840x2160) { return .hd4K3840x2160 }
|
|
911
975
|
if device.supportsSessionPreset(.hd1920x1080) { return .hd1920x1080 }
|
|
912
976
|
if device.supportsSessionPreset(.hd1280x720) { return .hd1280x720 }
|
|
913
977
|
if device.supportsSessionPreset(.high) { return .high }
|
|
914
|
-
if device.supportsSessionPreset(.photo) { return .photo }
|
|
978
|
+
if device.supportsSessionPreset(.photo) { return .photo }
|
|
915
979
|
return .vga640x480
|
|
916
980
|
} else {
|
|
917
|
-
// 4:3 or unknown: prefer photo → high → 1080p → 720p → vga
|
|
918
981
|
if device.supportsSessionPreset(.photo) { return .photo }
|
|
919
982
|
if device.supportsSessionPreset(.high) { return .high }
|
|
920
983
|
if device.supportsSessionPreset(.hd1920x1080) { return .hd1920x1080 }
|
|
@@ -1048,7 +1111,7 @@ extension CameraController {
|
|
|
1048
1111
|
if let connection = photoOutput.connection(with: .video) {
|
|
1049
1112
|
let captureOrientation = self.getPhysicalOrientation()
|
|
1050
1113
|
self.lastCaptureOrientation = captureOrientation
|
|
1051
|
-
|
|
1114
|
+
self.setVideoOrientation(captureOrientation, on: connection)
|
|
1052
1115
|
}
|
|
1053
1116
|
let settings = AVCapturePhotoSettings()
|
|
1054
1117
|
// Configure photo capture settings optimized for speed
|
|
@@ -2384,7 +2447,7 @@ extension CameraController {
|
|
|
2384
2447
|
}
|
|
2385
2448
|
}
|
|
2386
2449
|
|
|
2387
|
-
func captureVideo() throws {
|
|
2450
|
+
func captureVideo(maxDuration: Float? = nil, maxFileSize: Int? = nil) throws {
|
|
2388
2451
|
guard let captureSession = self.captureSession, captureSession.isRunning else {
|
|
2389
2452
|
throw CameraControllerError.captureSessionIsMissing
|
|
2390
2453
|
}
|
|
@@ -2426,7 +2489,7 @@ extension CameraController {
|
|
|
2426
2489
|
if let connection = fileVideoOutput.connection(with: .video) {
|
|
2427
2490
|
if connection.isEnabled == false { connection.isEnabled = true }
|
|
2428
2491
|
// Goes off accelerometer now
|
|
2429
|
-
|
|
2492
|
+
self.setVideoOrientation(self.getPhysicalOrientation(), on: connection)
|
|
2430
2493
|
|
|
2431
2494
|
// Front camera: mirror the recorded video so it looks natural (selfie style).
|
|
2432
2495
|
if self.currentCameraPosition == .front, connection.isVideoMirroringSupported {
|
|
@@ -2444,6 +2507,19 @@ extension CameraController {
|
|
|
2444
2507
|
let fileUrl = documentsDirectory.appendingPathComponent(fileName)
|
|
2445
2508
|
try? FileManager.default.removeItem(at: fileUrl)
|
|
2446
2509
|
|
|
2510
|
+
if let maxDuration = maxDuration, maxDuration > 0 {
|
|
2511
|
+
fileVideoOutput.maxRecordedDuration = CMTime(seconds: Double(maxDuration), preferredTimescale: 600)
|
|
2512
|
+
} else {
|
|
2513
|
+
fileVideoOutput.maxRecordedDuration = .invalid
|
|
2514
|
+
}
|
|
2515
|
+
fileVideoOutput.maxRecordedFileSize = Int64(maxFileSize ?? 0)
|
|
2516
|
+
|
|
2517
|
+
if let connection = fileVideoOutput.connection(with: .video),
|
|
2518
|
+
let codecType = avVideoCodecType(for: self.videoCodec),
|
|
2519
|
+
fileVideoOutput.availableVideoCodecTypes.contains(codecType) {
|
|
2520
|
+
fileVideoOutput.setOutputSettings([AVVideoCodecKey: codecType], for: connection)
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2447
2523
|
// Start recording video
|
|
2448
2524
|
fileVideoOutput.startRecording(to: fileUrl, recordingDelegate: self)
|
|
2449
2525
|
|
|
@@ -2857,11 +2933,33 @@ extension UIImage {
|
|
|
2857
2933
|
|
|
2858
2934
|
extension CameraController: AVCaptureFileOutputRecordingDelegate {
|
|
2859
2935
|
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2936
|
+
let reason = recordingFinishReason(from: error)
|
|
2937
|
+
if reason == "error" {
|
|
2938
|
+
print("Error recording movie: \(error?.localizedDescription ?? "unknown")")
|
|
2939
|
+
return
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
print("Movie recorded successfully: \(outputFileURL)")
|
|
2943
|
+
self.recordingFinishedCallback?(outputFileURL, reason)
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
private func recordingFinishReason(from error: Error?) -> String {
|
|
2947
|
+
guard let error = error else {
|
|
2948
|
+
return "manual"
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
let nsError = error as NSError
|
|
2952
|
+
guard nsError.domain == AVFoundationErrorDomain else {
|
|
2953
|
+
return "error"
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
switch AVError.Code(rawValue: nsError.code) {
|
|
2957
|
+
case .maximumDurationReached:
|
|
2958
|
+
return "maxDuration"
|
|
2959
|
+
case .maximumFileSizeReached:
|
|
2960
|
+
return "maxFileSize"
|
|
2961
|
+
default:
|
|
2962
|
+
return "error"
|
|
2865
2963
|
}
|
|
2866
2964
|
}
|
|
2867
2965
|
}
|
|
@@ -35,7 +35,7 @@ extension UIWindow {
|
|
|
35
35
|
*/
|
|
36
36
|
@objc(CameraPreview)
|
|
37
37
|
public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelegate {
|
|
38
|
-
private let pluginVersion: String = "8.
|
|
38
|
+
private let pluginVersion: String = "8.5.1"
|
|
39
39
|
public let identifier = "CameraPreviewPlugin"
|
|
40
40
|
public let jsName = "CameraPreview"
|
|
41
41
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -51,6 +51,12 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
51
51
|
CAPPluginMethod(name: "setFlashMode", returnType: CAPPluginReturnPromise),
|
|
52
52
|
CAPPluginMethod(name: "startRecordVideo", returnType: CAPPluginReturnPromise),
|
|
53
53
|
CAPPluginMethod(name: "stopRecordVideo", returnType: CAPPluginReturnPromise),
|
|
54
|
+
CAPPluginMethod(name: "getSupportedVideoCodecs", returnType: CAPPluginReturnPromise),
|
|
55
|
+
CAPPluginMethod(name: "getVideoCodec", returnType: CAPPluginReturnPromise),
|
|
56
|
+
CAPPluginMethod(name: "setVideoCodec", returnType: CAPPluginReturnPromise),
|
|
57
|
+
CAPPluginMethod(name: "getSupportedVideoQualities", returnType: CAPPluginReturnPromise),
|
|
58
|
+
CAPPluginMethod(name: "getVideoQuality", returnType: CAPPluginReturnPromise),
|
|
59
|
+
CAPPluginMethod(name: "setVideoQuality", returnType: CAPPluginReturnPromise),
|
|
54
60
|
CAPPluginMethod(name: "getTempFilePath", returnType: CAPPluginReturnPromise),
|
|
55
61
|
CAPPluginMethod(name: "getSupportedPictureSizes", returnType: CAPPluginReturnPromise),
|
|
56
62
|
CAPPluginMethod(name: "isRunning", returnType: CAPPluginReturnPromise),
|
|
@@ -646,7 +652,7 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
646
652
|
print("[CameraPreview] 📋 Settings received:")
|
|
647
653
|
print(" - position: \(call.getString("position") ?? "rear")")
|
|
648
654
|
print(" - deviceId: \(call.getString("deviceId") ?? "nil")")
|
|
649
|
-
print(" -
|
|
655
|
+
print(" - enableVideoMode: \(call.getBool("enableVideoMode") ?? call.getBool("cameraMode") ?? false)")
|
|
650
656
|
print(" - width: \(call.getInt("width") ?? 0)")
|
|
651
657
|
print(" - height: \(call.getInt("height") ?? 0)")
|
|
652
658
|
print(" - x: \(call.getInt("x") ?? -1)")
|
|
@@ -718,7 +724,7 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
718
724
|
|
|
719
725
|
self.cameraPosition = call.getString("position") ?? "rear"
|
|
720
726
|
let deviceId = call.getString("deviceId")
|
|
721
|
-
let cameraMode = call.getBool("cameraMode") ?? false
|
|
727
|
+
let cameraMode = call.getBool("enableVideoMode") ?? call.getBool("cameraMode") ?? false
|
|
722
728
|
|
|
723
729
|
// Set width - use screen width if not provided or if 0
|
|
724
730
|
if let width = call.getInt("width"), width > 0 {
|
|
@@ -1595,9 +1601,62 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
1595
1601
|
}
|
|
1596
1602
|
}
|
|
1597
1603
|
|
|
1604
|
+
@objc func setVideoQuality(_ call: CAPPluginCall) {
|
|
1605
|
+
guard let quality = call.getString("quality") else {
|
|
1606
|
+
call.reject("quality is required")
|
|
1607
|
+
return
|
|
1608
|
+
}
|
|
1609
|
+
do {
|
|
1610
|
+
try self.cameraController.setVideoQuality(quality)
|
|
1611
|
+
call.resolve()
|
|
1612
|
+
} catch {
|
|
1613
|
+
call.reject("Failed to set video quality: \(error.localizedDescription)")
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
@objc func getVideoQuality(_ call: CAPPluginCall) {
|
|
1618
|
+
call.resolve(["quality": self.cameraController.getVideoQuality()])
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
@objc func getSupportedVideoQualities(_ call: CAPPluginCall) {
|
|
1622
|
+
call.resolve(["qualities": self.cameraController.getSupportedVideoQualities()])
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
@objc func setVideoCodec(_ call: CAPPluginCall) {
|
|
1626
|
+
guard let codec = call.getString("codec") else {
|
|
1627
|
+
call.reject("codec is required")
|
|
1628
|
+
return
|
|
1629
|
+
}
|
|
1630
|
+
do {
|
|
1631
|
+
try self.cameraController.setVideoCodec(codec)
|
|
1632
|
+
call.resolve()
|
|
1633
|
+
} catch {
|
|
1634
|
+
call.reject("Failed to set video codec: \(error.localizedDescription)")
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
@objc func getVideoCodec(_ call: CAPPluginCall) {
|
|
1639
|
+
call.resolve(["codec": self.cameraController.getVideoCodec()])
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
@objc func getSupportedVideoCodecs(_ call: CAPPluginCall) {
|
|
1643
|
+
call.resolve(["codecs": self.cameraController.getSupportedVideoCodecs()])
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1598
1646
|
@objc func startRecordVideo(_ call: CAPPluginCall) {
|
|
1647
|
+
let maxDuration = call.getFloat("maxDuration")
|
|
1648
|
+
let maxFileSize = call.getInt("maxFileSize")
|
|
1649
|
+
if let videoCodec = call.getString("videoCodec") {
|
|
1650
|
+
try? self.cameraController.setVideoCodec(videoCodec)
|
|
1651
|
+
}
|
|
1652
|
+
self.cameraController.recordingFinishedCallback = { [weak self] fileURL, reason in
|
|
1653
|
+
DispatchQueue.main.async {
|
|
1654
|
+
self?.notifyListeners("recordingFinished", data: ["videoFilePath": fileURL.absoluteString, "reason": reason])
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1599
1658
|
do {
|
|
1600
|
-
try self.cameraController.captureVideo()
|
|
1659
|
+
try self.cameraController.captureVideo(maxDuration: maxDuration, maxFileSize: maxFileSize)
|
|
1601
1660
|
call.resolve()
|
|
1602
1661
|
} catch {
|
|
1603
1662
|
call.reject(error.localizedDescription)
|
|
@@ -1616,7 +1675,7 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
1616
1675
|
return
|
|
1617
1676
|
}
|
|
1618
1677
|
|
|
1619
|
-
call.resolve(["videoFilePath": fileURL.absoluteString])
|
|
1678
|
+
call.resolve(["videoFilePath": fileURL.absoluteString, "reason": "manual"])
|
|
1620
1679
|
}
|
|
1621
1680
|
}
|
|
1622
1681
|
|