@capgo/camera-preview 8.5.1 → 8.6.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/README.md +82 -3
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +67 -0
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java +94 -1
- package/android/src/main/java/app/capgo/capacitor/camera/preview/model/CameraSessionConfiguration.java +9 -0
- package/dist/docs.json +167 -2
- package/dist/esm/definitions.d.ts +57 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +11 -1
- package/dist/esm/web.js +12 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +12 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +12 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/CameraController.swift +141 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +38 -1
- package/package.json +1 -1
|
@@ -57,7 +57,6 @@ class CameraController: NSObject {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
|
|
61
60
|
private func setVideoOrientation(_ orientation: AVCaptureVideoOrientation, on connection: AVCaptureConnection) {
|
|
62
61
|
guard connection.isVideoOrientationSupported else { return }
|
|
63
62
|
connection.videoOrientation = orientation
|
|
@@ -155,6 +154,7 @@ class CameraController: NSObject {
|
|
|
155
154
|
|
|
156
155
|
var videoQuality: String = "high"
|
|
157
156
|
var videoCodec: String = "avc1"
|
|
157
|
+
var videoStabilizationMode: String = "off"
|
|
158
158
|
|
|
159
159
|
private static let allVideoQualities = ["low", "medium", "high", "2160p", "1080p", "720p", "480p", "4:3"]
|
|
160
160
|
|
|
@@ -213,6 +213,138 @@ class CameraController: NSObject {
|
|
|
213
213
|
return self.videoCodec
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
func isVideoStabilizationSupported() -> Bool {
|
|
217
|
+
guard let connection = self.fileVideoOutput?.connection(with: .video) else {
|
|
218
|
+
return false
|
|
219
|
+
}
|
|
220
|
+
return connection.isVideoStabilizationSupported
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
func getSupportedVideoStabilizationModes() -> [String] {
|
|
224
|
+
guard let connection = self.fileVideoOutput?.connection(with: .video),
|
|
225
|
+
connection.isVideoStabilizationSupported else {
|
|
226
|
+
return ["off"]
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let savedPreferred = connection.preferredVideoStabilizationMode
|
|
230
|
+
var supported: [String] = []
|
|
231
|
+
|
|
232
|
+
for candidate in Self.allVideoStabilizationModeCandidates() {
|
|
233
|
+
connection.preferredVideoStabilizationMode = candidate.mode
|
|
234
|
+
if connection.preferredVideoStabilizationMode == candidate.mode {
|
|
235
|
+
supported.append(candidate.name)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
connection.preferredVideoStabilizationMode = savedPreferred
|
|
240
|
+
return supported.isEmpty ? ["off"] : supported
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
func getVideoStabilizationMode() -> String {
|
|
244
|
+
if let connection = self.fileVideoOutput?.connection(with: .video),
|
|
245
|
+
connection.isVideoStabilizationSupported {
|
|
246
|
+
return self.videoStabilizationModeString(from: connection.activeVideoStabilizationMode)
|
|
247
|
+
}
|
|
248
|
+
return self.videoStabilizationMode
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
func setVideoStabilizationMode(_ mode: String) throws {
|
|
252
|
+
if self.fileVideoOutput?.isRecording == true {
|
|
253
|
+
throw CameraControllerError.recordingInProgress
|
|
254
|
+
}
|
|
255
|
+
guard let avMode = self.avVideoStabilizationMode(from: mode) else {
|
|
256
|
+
throw CameraControllerError.invalidOperation
|
|
257
|
+
}
|
|
258
|
+
let supportedModes = self.getSupportedVideoStabilizationModes()
|
|
259
|
+
guard supportedModes.contains(mode) else {
|
|
260
|
+
throw CameraControllerError.invalidOperation
|
|
261
|
+
}
|
|
262
|
+
self.videoStabilizationMode = mode
|
|
263
|
+
self.applyVideoStabilizationMode(avMode)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private static func allVideoStabilizationModeCandidates() -> [(name: String, mode: AVCaptureVideoStabilizationMode)] {
|
|
267
|
+
var candidates: [(String, AVCaptureVideoStabilizationMode)] = [
|
|
268
|
+
("off", .off),
|
|
269
|
+
("standard", .standard),
|
|
270
|
+
("cinematic", .cinematic),
|
|
271
|
+
("cinematicExtended", .cinematicExtended),
|
|
272
|
+
("auto", .auto),
|
|
273
|
+
]
|
|
274
|
+
if #available(iOS 17.0, *) {
|
|
275
|
+
candidates.append(("previewOptimized", .previewOptimized))
|
|
276
|
+
}
|
|
277
|
+
if #available(iOS 18.0, *) {
|
|
278
|
+
candidates.append(("cinematicExtendedEnhanced", .cinematicExtendedEnhanced))
|
|
279
|
+
}
|
|
280
|
+
return candidates
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private func applyVideoStabilizationMode(_ mode: AVCaptureVideoStabilizationMode? = nil) {
|
|
284
|
+
guard let connection = self.fileVideoOutput?.connection(with: .video),
|
|
285
|
+
connection.isVideoStabilizationSupported else {
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
let targetMode = mode ?? self.avVideoStabilizationMode(from: self.videoStabilizationMode) ?? .off
|
|
289
|
+
connection.preferredVideoStabilizationMode = targetMode
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private func avVideoStabilizationMode(from mode: String) -> AVCaptureVideoStabilizationMode? {
|
|
293
|
+
switch mode {
|
|
294
|
+
case "off":
|
|
295
|
+
return .off
|
|
296
|
+
case "standard":
|
|
297
|
+
return .standard
|
|
298
|
+
case "cinematic":
|
|
299
|
+
return .cinematic
|
|
300
|
+
case "cinematicExtended":
|
|
301
|
+
return .cinematicExtended
|
|
302
|
+
case "auto":
|
|
303
|
+
return .auto
|
|
304
|
+
case "previewOptimized":
|
|
305
|
+
if #available(iOS 17.0, *) {
|
|
306
|
+
return .previewOptimized
|
|
307
|
+
}
|
|
308
|
+
return nil
|
|
309
|
+
case "cinematicExtendedEnhanced":
|
|
310
|
+
if #available(iOS 18.0, *) {
|
|
311
|
+
return .cinematicExtendedEnhanced
|
|
312
|
+
}
|
|
313
|
+
return nil
|
|
314
|
+
case "lowLatency":
|
|
315
|
+
return nil
|
|
316
|
+
default:
|
|
317
|
+
return nil
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private func videoStabilizationModeString(from mode: AVCaptureVideoStabilizationMode) -> String {
|
|
322
|
+
switch mode {
|
|
323
|
+
case .off:
|
|
324
|
+
return "off"
|
|
325
|
+
case .standard:
|
|
326
|
+
return "standard"
|
|
327
|
+
case .cinematic:
|
|
328
|
+
return "cinematic"
|
|
329
|
+
case .cinematicExtended:
|
|
330
|
+
return "cinematicExtended"
|
|
331
|
+
case .auto:
|
|
332
|
+
return "auto"
|
|
333
|
+
default:
|
|
334
|
+
if #available(iOS 17.0, *) {
|
|
335
|
+
if mode == .previewOptimized {
|
|
336
|
+
return "previewOptimized"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if #available(iOS 18.0, *) {
|
|
340
|
+
if mode == .cinematicExtendedEnhanced {
|
|
341
|
+
return "cinematicExtendedEnhanced"
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return self.videoStabilizationMode
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
216
348
|
private func avVideoCodecType(for codec: String) -> AVVideoCodecType? {
|
|
217
349
|
switch codec.lowercased() {
|
|
218
350
|
case "avc1":
|
|
@@ -515,6 +647,7 @@ extension CameraController {
|
|
|
515
647
|
captureSession.addOutput(fileVideoOutput)
|
|
516
648
|
// Set orientation immediately
|
|
517
649
|
self.setVideoOrientation(videoOrientation, on: fileVideoOutput.connections)
|
|
650
|
+
self.applyVideoStabilizationMode()
|
|
518
651
|
}
|
|
519
652
|
|
|
520
653
|
// Set up preview layer session in the same configuration block
|
|
@@ -1094,6 +1227,7 @@ extension CameraController {
|
|
|
1094
1227
|
captureSession.removeOutput(fileVideoOutput)
|
|
1095
1228
|
if captureSession.canAddOutput(fileVideoOutput) {
|
|
1096
1229
|
captureSession.addOutput(fileVideoOutput)
|
|
1230
|
+
self.applyVideoStabilizationMode()
|
|
1097
1231
|
}
|
|
1098
1232
|
}
|
|
1099
1233
|
|
|
@@ -2479,6 +2613,7 @@ extension CameraController {
|
|
|
2479
2613
|
captureSession.beginConfiguration()
|
|
2480
2614
|
if captureSession.canAddOutput(fileVideoOutput) {
|
|
2481
2615
|
captureSession.addOutput(fileVideoOutput)
|
|
2616
|
+
self.applyVideoStabilizationMode()
|
|
2482
2617
|
} else {
|
|
2483
2618
|
captureSession.commitConfiguration()
|
|
2484
2619
|
throw CameraControllerError.invalidOperation
|
|
@@ -2520,6 +2655,8 @@ extension CameraController {
|
|
|
2520
2655
|
fileVideoOutput.setOutputSettings([AVVideoCodecKey: codecType], for: connection)
|
|
2521
2656
|
}
|
|
2522
2657
|
|
|
2658
|
+
self.applyVideoStabilizationMode()
|
|
2659
|
+
|
|
2523
2660
|
// Start recording video
|
|
2524
2661
|
fileVideoOutput.startRecording(to: fileUrl, recordingDelegate: self)
|
|
2525
2662
|
|
|
@@ -2824,6 +2961,7 @@ enum CameraControllerError: Swift.Error {
|
|
|
2824
2961
|
case invalidOperation
|
|
2825
2962
|
case noCamerasAvailable
|
|
2826
2963
|
case cannotFindDocumentsDirectory
|
|
2964
|
+
case recordingInProgress
|
|
2827
2965
|
case fileVideoOutputNotFound
|
|
2828
2966
|
case unknown
|
|
2829
2967
|
case invalidZoomLevel(min: CGFloat, max: CGFloat, requested: CGFloat)
|
|
@@ -2851,6 +2989,8 @@ extension CameraControllerError: LocalizedError {
|
|
|
2851
2989
|
return NSLocalizedString("Unknown", comment: "Unknown")
|
|
2852
2990
|
case .cannotFindDocumentsDirectory:
|
|
2853
2991
|
return NSLocalizedString("Cannot find documents directory", comment: "This should never happen")
|
|
2992
|
+
case .recordingInProgress:
|
|
2993
|
+
return NSLocalizedString("Cannot change video stabilization while recording is in progress.", comment: "Recording in progress")
|
|
2854
2994
|
case .fileVideoOutputNotFound:
|
|
2855
2995
|
return NSLocalizedString("Video recording is not available. Make sure the camera is properly initialized.", comment: "Video recording not available")
|
|
2856
2996
|
case .invalidZoomLevel(let min, let max, let requested):
|
|
@@ -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.6.0"
|
|
39
39
|
public let identifier = "CameraPreviewPlugin"
|
|
40
40
|
public let jsName = "CameraPreview"
|
|
41
41
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -54,6 +54,10 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
54
54
|
CAPPluginMethod(name: "getSupportedVideoCodecs", returnType: CAPPluginReturnPromise),
|
|
55
55
|
CAPPluginMethod(name: "getVideoCodec", returnType: CAPPluginReturnPromise),
|
|
56
56
|
CAPPluginMethod(name: "setVideoCodec", returnType: CAPPluginReturnPromise),
|
|
57
|
+
CAPPluginMethod(name: "isVideoStabilizationSupported", returnType: CAPPluginReturnPromise),
|
|
58
|
+
CAPPluginMethod(name: "getSupportedVideoStabilizationModes", returnType: CAPPluginReturnPromise),
|
|
59
|
+
CAPPluginMethod(name: "getVideoStabilizationMode", returnType: CAPPluginReturnPromise),
|
|
60
|
+
CAPPluginMethod(name: "setVideoStabilizationMode", returnType: CAPPluginReturnPromise),
|
|
57
61
|
CAPPluginMethod(name: "getSupportedVideoQualities", returnType: CAPPluginReturnPromise),
|
|
58
62
|
CAPPluginMethod(name: "getVideoQuality", returnType: CAPPluginReturnPromise),
|
|
59
63
|
CAPPluginMethod(name: "setVideoQuality", returnType: CAPPluginReturnPromise),
|
|
@@ -1643,12 +1647,45 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
1643
1647
|
call.resolve(["codecs": self.cameraController.getSupportedVideoCodecs()])
|
|
1644
1648
|
}
|
|
1645
1649
|
|
|
1650
|
+
@objc func isVideoStabilizationSupported(_ call: CAPPluginCall) {
|
|
1651
|
+
call.resolve(["supported": self.cameraController.isVideoStabilizationSupported()])
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
@objc func getSupportedVideoStabilizationModes(_ call: CAPPluginCall) {
|
|
1655
|
+
call.resolve(["modes": self.cameraController.getSupportedVideoStabilizationModes()])
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
@objc func getVideoStabilizationMode(_ call: CAPPluginCall) {
|
|
1659
|
+
call.resolve(["mode": self.cameraController.getVideoStabilizationMode()])
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
@objc func setVideoStabilizationMode(_ call: CAPPluginCall) {
|
|
1663
|
+
guard let mode = call.getString("mode") else {
|
|
1664
|
+
call.reject("mode is required")
|
|
1665
|
+
return
|
|
1666
|
+
}
|
|
1667
|
+
do {
|
|
1668
|
+
try self.cameraController.setVideoStabilizationMode(mode)
|
|
1669
|
+
call.resolve()
|
|
1670
|
+
} catch {
|
|
1671
|
+
call.reject("Failed to set video stabilization mode: \(error.localizedDescription)")
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1646
1675
|
@objc func startRecordVideo(_ call: CAPPluginCall) {
|
|
1647
1676
|
let maxDuration = call.getFloat("maxDuration")
|
|
1648
1677
|
let maxFileSize = call.getInt("maxFileSize")
|
|
1649
1678
|
if let videoCodec = call.getString("videoCodec") {
|
|
1650
1679
|
try? self.cameraController.setVideoCodec(videoCodec)
|
|
1651
1680
|
}
|
|
1681
|
+
if let videoStabilizationMode = call.getString("videoStabilizationMode") {
|
|
1682
|
+
do {
|
|
1683
|
+
try self.cameraController.setVideoStabilizationMode(videoStabilizationMode)
|
|
1684
|
+
} catch {
|
|
1685
|
+
call.reject("Failed to set video stabilization mode: \(error.localizedDescription)")
|
|
1686
|
+
return
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1652
1689
|
self.cameraController.recordingFinishedCallback = { [weak self] fileURL, reason in
|
|
1653
1690
|
DispatchQueue.main.async {
|
|
1654
1691
|
self?.notifyListeners("recordingFinished", data: ["videoFilePath": fileURL.absoluteString, "reason": reason])
|